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
b8cdc054
Commit
b8cdc054
authored
Sep 04, 2025
by
Administrator
Browse files
added Story page
parent
1a5a30c9
Changes
16
Show whitespace changes
Inline
Side-by-side
src/api/storyService.js
0 → 100644
View file @
b8cdc054
import
apiClient
from
'
./index.js
'
/**
* 通用的获取所有分页数据的函数
* @param {Function} apiCall - API调用函数
* @param {Object} params - 查询参数
* @returns {Promise<Array>} 所有页面的数据数组
*/
const
fetchAllPages
=
async
(
apiCall
,
params
=
{})
=>
{
let
allData
=
[]
let
page
=
1
let
hasNext
=
true
console
.
log
(
'
Starting to fetch all pages for API call...
'
)
while
(
hasNext
)
{
try
{
const
currentParams
=
{
...
params
,
page
,
page_size
:
100
}
// 每页100条,减少请求次数
console
.
log
(
`Fetching page
${
page
}
with params:`
,
currentParams
)
const
response
=
await
apiCall
(
currentParams
)
if
(
response
&&
response
.
results
)
{
// 分页格式响应
allData
=
[...
allData
,
...
response
.
results
]
hasNext
=
!!
response
.
next
console
.
log
(
`Page
${
page
}
: Got
${
response
.
results
.
length
}
items, total so far:
${
allData
.
length
}
, hasNext:
${
hasNext
}
`
)
}
else
if
(
Array
.
isArray
(
response
))
{
// 非分页格式响应(向后兼容)
allData
=
response
hasNext
=
false
console
.
log
(
'
Non-paginated response detected, got all data at once:
'
,
allData
.
length
,
'
items
'
)
}
else
{
console
.
log
(
'
Unexpected response format, stopping pagination
'
)
hasNext
=
false
}
page
++
// 安全检查:防止无限循环
if
(
page
>
100
)
{
console
.
warn
(
'
Reached maximum page limit (100), stopping pagination
'
)
break
}
}
catch
(
error
)
{
console
.
error
(
`Error fetching page
${
page
}
:`
,
error
)
throw
error
}
}
console
.
log
(
`Finished fetching all pages. Total items:
${
allData
.
length
}
`
)
return
allData
}
// API端点定义
const
apiEndpoints
=
{
STORIES
:
{
LIST
:
'
/api/stories/
'
,
DETAIL
:
(
storyId
)
=>
`/api/stories/
${
storyId
}
/`
,
UPDATE
:
(
storyId
)
=>
`/api/stories/
${
storyId
}
/`
,
DELETE
:
(
storyId
)
=>
`/api/stories/
${
storyId
}
/`
}
}
/**
* 单页API调用函数(内部使用)
* @param {Object} params - 查询参数(包含分页参数)
* @returns {Promise} API响应
*/
const
getStoriesPage
=
async
(
params
=
{})
=>
{
const
response
=
await
apiClient
.
get
(
apiEndpoints
.
STORIES
.
LIST
,
{
params
})
return
response
.
data
}
/**
* 获取所有系列任务
* @param {Object} params - 查询参数
* @returns {Promise} 系列任务列表(完整数据,已处理分页)
*/
export
const
getStories
=
async
(
params
=
{})
=>
{
try
{
// 构建查询参数,过滤掉null和undefined值
const
queryParams
=
{}
Object
.
entries
(
params
).
forEach
(([
key
,
value
])
=>
{
if
(
value
!==
null
&&
value
!==
undefined
)
{
queryParams
[
key
]
=
value
}
})
console
.
log
(
'
Stories API request params:
'
,
queryParams
)
// 使用通用分页函数获取所有数据
const
allStories
=
await
fetchAllPages
(
getStoriesPage
,
queryParams
)
console
.
log
(
'
Stories API final result:
'
,
{
totalCount
:
allStories
.
length
,
sampleData
:
allStories
.
slice
(
0
,
3
)
// 显示前3条数据作为样例
})
return
allStories
}
catch
(
error
)
{
console
.
error
(
'
Failed to fetch stories:
'
,
error
)
throw
error
}
}
/**
* 根据ID获取系列任务
* @param {number} storyId - 系列任务ID
* @returns {Promise} 系列任务详情
*/
export
const
getStoryById
=
async
(
storyId
)
=>
{
try
{
const
response
=
await
apiClient
.
get
(
apiEndpoints
.
STORIES
.
DETAIL
(
storyId
))
console
.
log
(
'
Story detail API response:
'
,
response
.
data
)
return
response
.
data
}
catch
(
error
)
{
console
.
error
(
'
Failed to fetch story detail:
'
,
error
)
// 提供更详细的错误信息
if
(
error
.
response
)
{
// 服务器响应了错误状态码
throw
new
Error
(
`获取系列任务详情失败:
${
error
.
response
.
status
}
-
${
error
.
response
.
statusText
}
`
)
}
else
if
(
error
.
request
)
{
// 请求已发出但没有收到响应
throw
new
Error
(
'
网络错误: 无法连接到服务器
'
)
}
else
{
// 其他错误
throw
new
Error
(
`请求配置错误:
${
error
.
message
}
`
)
}
}
}
/**
* 创建新的系列任务
* @param {Object} storyData - 系列任务数据
* @returns {Promise} 创建的系列任务
*/
export
const
createStory
=
async
(
storyData
)
=>
{
try
{
const
response
=
await
apiClient
.
post
(
apiEndpoints
.
STORIES
.
LIST
,
storyData
)
console
.
log
(
'
Create story API response:
'
,
response
.
data
)
return
response
.
data
}
catch
(
error
)
{
console
.
error
(
'
Failed to create story:
'
,
error
)
// 提供更详细的错误信息
if
(
error
.
response
)
{
// 服务器响应了错误状态码
throw
new
Error
(
`创建系列任务失败:
${
error
.
response
.
status
}
-
${
error
.
response
.
statusText
}
`
)
}
else
if
(
error
.
request
)
{
// 请求已发出但没有收到响应
throw
new
Error
(
'
网络错误: 无法连接到服务器
'
)
}
else
{
// 其他错误
throw
new
Error
(
`请求配置错误:
${
error
.
message
}
`
)
}
}
}
/**
* 更新系列任务
* @param {number} storyId - 系列任务ID
* @param {Object} storyData - 更新的系列任务数据
* @returns {Promise} 更新后的系列任务
*/
export
const
updateStory
=
async
(
storyId
,
storyData
)
=>
{
try
{
const
response
=
await
apiClient
.
patch
(
apiEndpoints
.
STORIES
.
UPDATE
(
storyId
),
storyData
)
console
.
log
(
'
Update story API response:
'
,
response
.
data
)
return
response
.
data
}
catch
(
error
)
{
console
.
error
(
'
Failed to update story:
'
,
error
)
// 提供更详细的错误信息
if
(
error
.
response
)
{
// 服务器响应了错误状态码
throw
new
Error
(
`更新系列任务失败:
${
error
.
response
.
status
}
-
${
error
.
response
.
statusText
}
`
)
}
else
if
(
error
.
request
)
{
// 请求已发出但没有收到响应
throw
new
Error
(
'
网络错误: 无法连接到服务器
'
)
}
else
{
// 其他错误
throw
new
Error
(
`请求配置错误:
${
error
.
message
}
`
)
}
}
}
/**
* 删除系列任务
* @param {number} storyId - 系列任务ID
* @returns {Promise} 删除结果
*/
export
const
deleteStory
=
async
(
storyId
)
=>
{
try
{
const
response
=
await
apiClient
.
delete
(
apiEndpoints
.
STORIES
.
DELETE
(
storyId
))
console
.
log
(
'
Delete story API response:
'
,
response
.
data
)
return
response
.
data
}
catch
(
error
)
{
console
.
error
(
'
Failed to delete story:
'
,
error
)
// 提供更详细的错误信息
if
(
error
.
response
)
{
// 服务器响应了错误状态码
throw
new
Error
(
`删除系列任务失败:
${
error
.
response
.
status
}
-
${
error
.
response
.
statusText
}
`
)
}
else
if
(
error
.
request
)
{
// 请求已发出但没有收到响应
throw
new
Error
(
'
网络错误: 无法连接到服务器
'
)
}
else
{
// 其他错误
throw
new
Error
(
`请求配置错误:
${
error
.
message
}
`
)
}
}
}
src/components/AppDrawer.vue
View file @
b8cdc054
...
...
@@ -8,16 +8,23 @@
<v-list-item
:title=
"$t('navigation.welcome.title')"
:subtitle=
"$t('navigation.welcome.subtitle')"
></v-list-item>
<v-divider></v-divider>
<v-list
v-model:opened=
"opened"
>
<v-list
v-model:opened=
"opened"
nav
>
<template
v-for=
"item in filteredMenu"
:key=
"item.path || item.nameKey"
>
<!-- 分组(有 children):点击父项仅用于展开,不路由 -->
<v-list-group
v-if=
"item.children && item.children.length"
:value=
"item.path"
:prepend-icon=
"item.mdiIcon"
slim
>
<template
#activator
="
{ props }">
<v-list-item
v-bind=
"props"
>
<v-list-item
v-bind=
"props"
:ripple=
"true"
:disabled=
"false"
nav
density=
"comfortable"
>
<v-list-item-title
class=
"text-body-1"
>
{{
$t
(
item
.
nameKey
)
}}
</v-list-item-title>
</v-list-item>
</
template
>
...
...
@@ -30,6 +37,7 @@
:ripple=
"child.hasRouter"
:disabled=
"!child.hasRouter"
nav
density=
"compact"
>
<v-list-item-title
class=
"text-body-1"
>
{{ $t(child.nameKey) }}
</v-list-item-title>
</v-list-item>
...
...
@@ -43,13 +51,14 @@
:ripple=
"item.hasRouter"
:disabled=
"false"
@
click=
"!item.hasRouter ? noop() : undefined"
nav
>
nav
density=
"comfortable"
>
<v-list-item-title
class=
"text-body-1"
>
{{ $t(item.nameKey) }}
</v-list-item-title>
</v-list-item>
</template>
</v-list>
</v-navigation-drawer>
</template>
<
script
setup
>
...
...
@@ -104,4 +113,9 @@ const noop = () => {}
</
script
>
<
style
scoped
>
/* 使用Vuetify的类来确保一致的外观 */
.v-list-group--activator
.v-list-item
{
/* 确保分组标题与普通列表项有相同的密度设置 */
--v-list-item-density
:
-1
;
}
</
style
>
src/components/constants/menuItems.js
View file @
b8cdc054
...
...
@@ -34,6 +34,12 @@ export const GLOBAL_MENU_ITEMS = [
path
:
'
/master-data/terms
'
,
mdiIcon
:
'
mdi-calendar-month-outline
'
,
hasRouter
:
true
},
{
nameKey
:
'
navigation.menu.stories
'
,
path
:
'
/master-data/stories
'
,
mdiIcon
:
'
mdi-book-multiple-outline
'
,
hasRouter
:
true
}
]
},
...
...
src/locales/en/common/table.js
0 → 100644
View file @
b8cdc054
export
default
{
pagination
:
{
itemsPerPage
:
'
Items per page
'
,
itemsPerPageAll
:
'
All
'
,
noDataText
:
'
No data available
'
,
loadingText
:
'
Loading items...
'
,
pageText
:
'
{0}-{1} of {2}
'
,
rowsPerPageText
:
'
Rows per page:
'
},
actions
:
{
edit
:
'
Edit
'
,
delete
:
'
Delete
'
,
view
:
'
View
'
},
filter
:
{
search
:
'
Search
'
,
clear
:
'
Clear
'
,
apply
:
'
Apply
'
}
}
\ No newline at end of file
src/locales/en/index.js
View file @
b8cdc054
...
...
@@ -3,10 +3,12 @@ import buttons from './common/buttons'
import
messages
from
'
./common/messages
'
import
status
from
'
./common/status
'
import
validation
from
'
./common/validation
'
import
table
from
'
./common/table
'
import
student
from
'
./modules/student
'
import
subject
from
'
./modules/subject
'
import
term
from
'
./modules/term
'
import
task
from
'
./modules/task
'
import
story
from
'
./modules/story
'
import
auth
from
'
./modules/auth
'
import
navigation
from
'
./modules/navigation
'
import
appHeader
from
'
./components/app-header
'
...
...
@@ -17,12 +19,14 @@ export default {
buttons
,
messages
,
status
,
validation
validation
,
table
},
student
,
subject
,
term
,
task
,
story
,
auth
,
navigation
,
components
:
{
...
...
src/locales/en/modules/navigation.js
View file @
b8cdc054
...
...
@@ -12,6 +12,7 @@ export default {
students
:
'
Students
'
,
subjects
:
'
Subjects
'
,
terms
:
'
Terms
'
,
stories
:
'
Story
'
,
tasks
:
'
Task Management
'
,
dashboard
:
'
Dashboard
'
,
settings
:
'
Settings
'
,
...
...
@@ -24,6 +25,7 @@ export default {
students
:
'
Students
'
,
subjects
:
'
Subjects
'
,
terms
:
'
Terms
'
,
stories
:
'
Story
'
,
tasks
:
'
Task Management
'
,
profile
:
'
Profile
'
},
...
...
src/locales/en/modules/story.js
0 → 100644
View file @
b8cdc054
export
default
{
title
:
'
Story
'
,
tableHeader
:
{
storyId
:
'
Story ID
'
,
storyName
:
'
Story Name
'
,
description
:
'
Description
'
,
studentId
:
'
Student
'
,
subjectId
:
'
Subject
'
,
termId
:
'
Term
'
,
trackedFlag
:
'
Tracked
'
,
createdBy
:
'
Created By
'
,
creationDate
:
'
Creation Date
'
,
lastUpdatedBy
:
'
Last Updated By
'
,
lastUpdateDate
:
'
Last Update Date
'
,
actions
:
'
Actions
'
},
form
:
{
storyName
:
'
Story Name
'
,
description
:
'
Description
'
,
studentId
:
'
Student
'
,
subjectId
:
'
Subject
'
,
termId
:
'
Term
'
,
trackedFlag
:
'
Tracked
'
},
dialog
:
{
create
:
'
Create Story
'
,
edit
:
'
Edit Story
'
,
delete
:
'
Delete Story
'
},
button
:
{
create
:
'
Create Story
'
,
edit
:
'
Edit
'
,
delete
:
'
Delete
'
,
save
:
'
Save
'
,
cancel
:
'
Cancel
'
,
confirm
:
'
Confirm
'
},
message
:
{
createSuccess
:
'
Story created successfully
'
,
updateSuccess
:
'
Story updated successfully
'
,
deleteSuccess
:
'
Story deleted successfully
'
,
createError
:
'
Failed to create story
'
,
updateError
:
'
Failed to update story
'
,
deleteError
:
'
Failed to delete story
'
,
loadError
:
'
Failed to load stories
'
},
deleteConfirm
:
{
title
:
'
Delete Story
'
,
message
:
'
Are you sure you want to delete this story?
'
,
description
:
'
This action cannot be undone.
'
,
warning
:
'
This action cannot be undone. Please proceed with caution.
'
,
storyInfo
:
'
{name}
'
},
validation
:
{
storyNameRequired
:
'
Story name is required
'
,
studentRequired
:
'
Student is required
'
,
subjectRequired
:
'
Subject is required
'
,
termRequired
:
'
Term is required
'
},
status
:
{
tracked
:
'
Tracked
'
,
notTracked
:
'
Not Tracked
'
,
enabled
:
'
Enabled
'
,
disabled
:
'
Disabled
'
}
}
src/locales/zh-CN/common/table.js
0 → 100644
View file @
b8cdc054
export
default
{
pagination
:
{
itemsPerPage
:
'
每页显示
'
,
itemsPerPageAll
:
'
全部
'
,
noDataText
:
'
暂无数据
'
,
loadingText
:
'
加载中...
'
,
pageText
:
'
{0}-{1} 共 {2}
'
,
rowsPerPageText
:
'
每页行数:
'
},
actions
:
{
edit
:
'
编辑
'
,
delete
:
'
删除
'
,
view
:
'
查看
'
},
filter
:
{
search
:
'
搜索
'
,
clear
:
'
清除
'
,
apply
:
'
应用
'
}
}
\ No newline at end of file
src/locales/zh-CN/index.js
View file @
b8cdc054
...
...
@@ -3,10 +3,12 @@ import buttons from './common/buttons'
import
messages
from
'
./common/messages
'
import
status
from
'
./common/status
'
import
validation
from
'
./common/validation
'
import
table
from
'
./common/table
'
import
student
from
'
./modules/student
'
import
subject
from
'
./modules/subject
'
import
term
from
'
./modules/term
'
import
task
from
'
./modules/task
'
import
story
from
'
./modules/story
'
import
auth
from
'
./modules/auth
'
import
navigation
from
'
./modules/navigation
'
import
appHeader
from
'
./components/app-header
'
...
...
@@ -17,12 +19,14 @@ export default {
buttons
,
messages
,
status
,
validation
validation
,
table
},
student
,
subject
,
term
,
task
,
story
,
auth
,
navigation
,
components
:
{
...
...
src/locales/zh-CN/modules/navigation.js
View file @
b8cdc054
...
...
@@ -12,6 +12,7 @@ export default {
students
:
'
学生
'
,
subjects
:
'
学科
'
,
terms
:
'
学期
'
,
stories
:
'
系列任务
'
,
tasks
:
'
作业管理
'
,
dashboard
:
'
仪表盘
'
,
settings
:
'
设置
'
,
...
...
@@ -24,6 +25,7 @@ export default {
students
:
'
学生
'
,
subjects
:
'
学科
'
,
terms
:
'
学期
'
,
stories
:
'
系列任务
'
,
tasks
:
'
作业管理
'
,
profile
:
'
个人资料
'
},
...
...
src/locales/zh-CN/modules/story.js
0 → 100644
View file @
b8cdc054
export
default
{
title
:
'
系列任务
'
,
tableHeader
:
{
storyId
:
'
系列任务ID
'
,
storyName
:
'
系列任务名称
'
,
description
:
'
描述
'
,
studentId
:
'
学生
'
,
subjectId
:
'
学科
'
,
termId
:
'
学期
'
,
trackedFlag
:
'
跟踪状态
'
,
createdBy
:
'
创建人
'
,
creationDate
:
'
创建日期
'
,
lastUpdatedBy
:
'
最后更新人
'
,
lastUpdateDate
:
'
最后更新日期
'
,
actions
:
'
操作
'
},
form
:
{
storyName
:
'
系列任务名称
'
,
description
:
'
描述
'
,
studentId
:
'
学生
'
,
subjectId
:
'
学科
'
,
termId
:
'
学期
'
,
trackedFlag
:
'
跟踪状态
'
},
dialog
:
{
create
:
'
创建系列任务
'
,
edit
:
'
编辑系列任务
'
,
delete
:
'
删除系列任务
'
},
button
:
{
create
:
'
创建系列任务
'
,
edit
:
'
编辑
'
,
delete
:
'
删除
'
,
save
:
'
保存
'
,
cancel
:
'
取消
'
,
confirm
:
'
确认
'
},
message
:
{
createSuccess
:
'
系列任务创建成功
'
,
updateSuccess
:
'
系列任务更新成功
'
,
deleteSuccess
:
'
系列任务删除成功
'
,
createError
:
'
创建系列任务失败
'
,
updateError
:
'
更新系列任务失败
'
,
deleteError
:
'
删除系列任务失败
'
,
loadError
:
'
加载系列任务失败
'
},
deleteConfirm
:
{
title
:
'
删除系列任务
'
,
message
:
'
确定要删除此系列任务吗?
'
,
description
:
'
此操作无法撤销。
'
,
warning
:
'
此操作无法撤销,请谨慎操作。
'
,
storyInfo
:
'
{name}
'
},
validation
:
{
storyNameRequired
:
'
系列任务名称不能为空
'
,
studentRequired
:
'
学生不能为空
'
,
subjectRequired
:
'
学科不能为空
'
,
termRequired
:
'
学期不能为空
'
},
status
:
{
tracked
:
'
已跟踪
'
,
notTracked
:
'
未跟踪
'
,
enabled
:
'
启用
'
,
disabled
:
'
禁用
'
}
}
src/router/index.js
View file @
b8cdc054
...
...
@@ -6,6 +6,7 @@ import ProfileView from '../views/ProfileView.vue'
import
StudentView
from
'
../views/StudentView.vue
'
import
SubjectView
from
'
../views/SubjectView.vue
'
import
TermView
from
'
../views/TermView.vue
'
import
StoryView
from
'
../views/StoryView.vue
'
import
TaskView
from
'
../views/TaskView.vue
'
const
router
=
createRouter
({
...
...
@@ -84,8 +85,7 @@ const router = createRouter({
]
},
},
{
path
:
'
/master-data/terms
'
,
{
path
:
'
/master-data/terms
'
,
name
:
'
terms
'
,
component
:
TermView
,
meta
:
{
...
...
@@ -102,6 +102,24 @@ const router = createRouter({
]
},
},
{
path
:
'
/master-data/stories
'
,
name
:
'
stories
'
,
component
:
StoryView
,
meta
:
{
requiresAuth
:
true
,
layout
:
'
default
'
,
title
:
'
Stories
'
,
breadcrumb
:
{
key
:
'
navigation.breadcrumb.stories
'
},
breadcrumbPath
:
[
{
key
:
'
navigation.breadcrumb.home
'
,
to
:
'
/
'
,
disabled
:
false
},
{
key
:
'
navigation.breadcrumb.masterData
'
,
to
:
null
,
disabled
:
true
},
{
key
:
'
navigation.breadcrumb.stories
'
,
to
:
'
/master-data/stories
'
,
disabled
:
true
}
]
},
},
{
path
:
'
/tasks
'
,
name
:
'
tasks
'
,
...
...
src/views/StoryView.vue
0 → 100644
View file @
b8cdc054
<
script
setup
>
import
{
onMounted
,
ref
,
computed
}
from
'
vue
'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
useAuthStore
}
from
'
@/stores/auth
'
import
{
getStories
,
updateStory
,
createStory
,
deleteStory
}
from
'
@/api/storyService.js
'
import
{
getStudents
}
from
'
@/api/studentService.js
'
import
{
getSubjects
}
from
'
@/api/subjectService.js
'
import
{
getTerms
}
from
'
@/api/termService.js
'
const
{
t
}
=
useI18n
()
const
authStore
=
useAuthStore
()
const
isLoading
=
ref
(
false
)
const
isError
=
ref
(
false
)
const
errorMessage
=
ref
(
''
)
const
stories
=
ref
([])
// 关联数据
const
students
=
ref
([])
const
subjects
=
ref
([])
const
terms
=
ref
([])
const
isLoadingRelatedData
=
ref
(
false
)
// 编辑功能相关状态
const
editDialog
=
ref
(
false
)
const
editingStory
=
ref
(
null
)
const
editForm
=
ref
({
story_name
:
''
,
description
:
''
,
student_id
:
null
,
subject_id
:
null
,
term_id
:
null
,
tracked_flag
:
'
N
'
})
const
isSaving
=
ref
(
false
)
const
successMessage
=
ref
(
''
)
const
saveErrorMessage
=
ref
(
''
)
// 新增功能相关状态
const
createDialog
=
ref
(
false
)
const
createForm
=
ref
({
story_name
:
''
,
description
:
''
,
student_id
:
null
,
subject_id
:
null
,
term_id
:
null
,
tracked_flag
:
'
N
'
})
const
isCreating
=
ref
(
false
)
const
createErrorMessage
=
ref
(
''
)
// 删除功能相关状态
const
deleteDialog
=
ref
(
false
)
const
storyToDelete
=
ref
(
null
)
const
isDeleting
=
ref
(
false
)
const
headers
=
computed
(()
=>
[
{
title
:
t
(
'
story.tableHeader.storyName
'
),
value
:
'
story_name
'
,
sortable
:
true
,
width
:
'
20%
'
},
{
title
:
t
(
'
story.tableHeader.description
'
),
value
:
'
description
'
,
sortable
:
false
,
width
:
'
20%
'
},
{
title
:
t
(
'
story.tableHeader.studentId
'
),
value
:
'
student_id
'
,
sortable
:
true
,
width
:
'
15%
'
},
{
title
:
t
(
'
story.tableHeader.subjectId
'
),
value
:
'
subject_id
'
,
sortable
:
true
,
width
:
'
15%
'
},
{
title
:
t
(
'
story.tableHeader.termId
'
),
value
:
'
term_id
'
,
sortable
:
true
,
width
:
'
15%
'
},
{
title
:
t
(
'
story.tableHeader.trackedFlag
'
),
value
:
'
tracked_flag
'
,
sortable
:
true
,
width
:
'
15%
'
,
sort
:
(
a
,
b
)
=>
{
if
(
a
===
'
Y
'
&&
b
===
'
N
'
)
return
-
1
if
(
a
===
'
N
'
&&
b
===
'
Y
'
)
return
1
return
0
}
},
{
title
:
t
(
'
story.tableHeader.actions
'
),
value
:
'
actions
'
,
sortable
:
false
,
align
:
'
end
'
,
width
:
'
12%
'
},
])
// 获取系列任务数据
const
fetchStories
=
async
()
=>
{
isLoading
.
value
=
true
isError
.
value
=
false
errorMessage
.
value
=
''
try
{
const
storiesData
=
await
getStories
()
console
.
log
(
'
Stories data:
'
,
storiesData
)
stories
.
value
=
Array
.
isArray
(
storiesData
)
?
storiesData
:
[]
// 为每个story添加关联数据的名称
stories
.
value
.
forEach
(
story
=>
{
story
.
student_name
=
getStudentName
(
story
.
student_id
)
story
.
subject_name
=
getSubjectName
(
story
.
subject_id
)
story
.
term_name
=
getTermName
(
story
.
term_id
)
})
}
catch
(
e
)
{
isError
.
value
=
true
errorMessage
.
value
=
e
?.
response
?.
data
?.
detail
||
e
?.
message
||
t
(
'
story.message.loadError
'
)
// 3秒后自动隐藏错误消息
setTimeout
(()
=>
{
isError
.
value
=
false
errorMessage
.
value
=
''
},
3000
)
}
finally
{
isLoading
.
value
=
false
}
}
// 获取关联数据(学生、学科、学期)
const
fetchRelatedData
=
async
()
=>
{
isLoadingRelatedData
.
value
=
true
try
{
const
[
studentsData
,
subjectsData
,
termsData
]
=
await
Promise
.
all
([
getStudents
(),
getSubjects
(),
getTerms
()
])
students
.
value
=
studentsData
subjects
.
value
=
subjectsData
terms
.
value
=
termsData
console
.
log
(
'
Related data loaded:
'
,
{
students
:
students
.
value
.
length
,
subjects
:
subjects
.
value
.
length
,
terms
:
terms
.
value
.
length
})
}
catch
(
error
)
{
console
.
error
(
'
Failed to load related data:
'
,
error
)
}
finally
{
isLoadingRelatedData
.
value
=
false
}
}
onMounted
(()
=>
{
// 确保 axios 鉴权拦截器与头已设置
if
(
authStore
&&
authStore
.
initializeAuth
)
{
authStore
.
initializeAuth
()
}
fetchStories
()
fetchRelatedData
()
})
// 编辑功能方法
const
openEditDialog
=
(
story
)
=>
{
editingStory
.
value
=
story
editForm
.
value
=
{
story_name
:
story
.
story_name
||
''
,
description
:
story
.
description
||
''
,
student_id
:
story
.
student_id
||
null
,
subject_id
:
story
.
subject_id
||
null
,
term_id
:
story
.
term_id
||
null
,
tracked_flag
:
story
.
tracked_flag
||
'
N
'
}
console
.
log
(
'
Editing story data:
'
,
story
)
console
.
log
(
'
Converted form data:
'
,
editForm
.
value
)
editDialog
.
value
=
true
}
const
closeEditDialog
=
()
=>
{
editDialog
.
value
=
false
editingStory
.
value
=
null
editForm
.
value
=
{
story_name
:
''
,
description
:
''
,
student_id
:
null
,
subject_id
:
null
,
term_id
:
null
,
tracked_flag
:
'
N
'
}
}
const
saveStory
=
async
()
=>
{
// 检查是否有正在编辑的故事以及故事ID
if
(
!
editingStory
.
value
?.
story_id
)
{
saveErrorMessage
.
value
=
t
(
'
common.messages.error.general
'
)
// 3秒后自动隐藏错误消息
setTimeout
(()
=>
{
saveErrorMessage
.
value
=
''
},
3000
)
return
}
isSaving
.
value
=
true
saveErrorMessage
.
value
=
''
successMessage
.
value
=
''
try
{
console
.
log
(
'
Form data before update:
'
,
editForm
.
value
)
// 构建要发送的数据
const
updateData
=
{
story_name
:
editForm
.
value
.
story_name
,
description
:
editForm
.
value
.
description
,
student_id
:
editForm
.
value
.
student_id
,
subject_id
:
editForm
.
value
.
subject_id
,
term_id
:
editForm
.
value
.
term_id
,
tracked_flag
:
editForm
.
value
.
tracked_flag
}
console
.
log
(
'
Sending data:
'
,
updateData
)
// 1. 调用API更新系列任务
await
updateStory
(
editingStory
.
value
.
story_id
,
updateData
)
// 2. 更新本地数据
const
index
=
stories
.
value
.
findIndex
(
s
=>
s
.
story_id
===
editingStory
.
value
.
story_id
)
if
(
index
!==
-
1
)
{
stories
.
value
[
index
]
=
{
...
stories
.
value
[
index
],
...
updateData
}
}
// 3. 显示成功消息
successMessage
.
value
=
t
(
'
story.message.updateSuccess
'
)
setTimeout
(()
=>
{
successMessage
.
value
=
''
},
3000
)
closeEditDialog
()
}
catch
(
error
)
{
console
.
error
(
'
Update failed:
'
,
error
)
saveErrorMessage
.
value
=
error
.
message
||
t
(
'
story.message.updateError
'
)
setTimeout
(()
=>
{
saveErrorMessage
.
value
=
''
},
3000
)
}
finally
{
isSaving
.
value
=
false
}
}
// 新增功能方法
const
openCreateDialog
=
()
=>
{
createForm
.
value
=
{
story_name
:
''
,
description
:
''
,
student_id
:
null
,
subject_id
:
null
,
term_id
:
null
,
tracked_flag
:
'
N
'
}
createErrorMessage
.
value
=
''
createDialog
.
value
=
true
}
const
closeCreateDialog
=
()
=>
{
createDialog
.
value
=
false
createForm
.
value
=
{
story_name
:
''
,
description
:
''
,
student_id
:
null
,
subject_id
:
null
,
term_id
:
null
,
tracked_flag
:
'
N
'
}
createErrorMessage
.
value
=
''
}
const
createNewStory
=
async
()
=>
{
isCreating
.
value
=
true
createErrorMessage
.
value
=
''
successMessage
.
value
=
''
try
{
console
.
log
(
'
Form data before creation:
'
,
createForm
.
value
)
// 构建要发送的数据
const
createData
=
{
story_name
:
createForm
.
value
.
story_name
,
description
:
createForm
.
value
.
description
,
student_id
:
createForm
.
value
.
student_id
,
subject_id
:
createForm
.
value
.
subject_id
,
term_id
:
createForm
.
value
.
term_id
,
tracked_flag
:
createForm
.
value
.
tracked_flag
}
console
.
log
(
'
Sending data:
'
,
createData
)
// 1. 调用API创建系列任务
const
newStory
=
await
createStory
(
createData
)
// 2. 创建成功后,将新系列任务添加到本地列表
const
storyToAdd
=
{
...
newStory
,
...
createData
}
stories
.
value
.
unshift
(
storyToAdd
)
// 在列表顶部添加新系列任务
// 3. 显示成功消息
successMessage
.
value
=
t
(
'
story.message.createSuccess
'
)
setTimeout
(()
=>
{
successMessage
.
value
=
''
},
3000
)
closeCreateDialog
()
}
catch
(
error
)
{
console
.
error
(
'
Creation failed:
'
,
error
)
createErrorMessage
.
value
=
error
.
message
||
t
(
'
story.message.createError
'
)
setTimeout
(()
=>
{
createErrorMessage
.
value
=
''
},
3000
)
}
finally
{
isCreating
.
value
=
false
}
}
// 删除功能方法
const
openDeleteDialog
=
(
story
)
=>
{
storyToDelete
.
value
=
story
deleteDialog
.
value
=
true
}
const
closeDeleteDialog
=
()
=>
{
deleteDialog
.
value
=
false
storyToDelete
.
value
=
null
}
const
confirmDelete
=
async
()
=>
{
if
(
!
storyToDelete
.
value
?.
story_id
)
{
console
.
error
(
'
Unable to get story ID
'
)
return
}
isDeleting
.
value
=
true
try
{
console
.
log
(
'
Deleting story:
'
,
storyToDelete
.
value
)
// 1. 调用API删除系列任务
await
deleteStory
(
storyToDelete
.
value
.
story_id
)
// 2. 从本地列表中移除
const
index
=
stories
.
value
.
findIndex
(
s
=>
s
.
story_id
===
storyToDelete
.
value
.
story_id
)
if
(
index
!==
-
1
)
{
stories
.
value
.
splice
(
index
,
1
)
}
// 3. 显示成功消息
successMessage
.
value
=
t
(
'
story.message.deleteSuccess
'
)
setTimeout
(()
=>
{
successMessage
.
value
=
''
},
3000
)
closeDeleteDialog
()
closeEditDialog
()
// 自动关闭编辑对话框
}
catch
(
error
)
{
console
.
error
(
'
Deletion failed:
'
,
error
)
// 在删除对话框中显示错误,但不关闭对话框
saveErrorMessage
.
value
=
error
.
message
||
t
(
'
story.message.deleteError
'
)
setTimeout
(()
=>
{
saveErrorMessage
.
value
=
''
},
3000
)
}
finally
{
isDeleting
.
value
=
false
}
}
// 格式化显示
const
getStudentName
=
(
studentId
)
=>
{
const
student
=
students
.
value
.
find
(
s
=>
s
.
student_id
===
studentId
)
return
student
?
student
.
student_name
:
studentId
}
const
getSubjectName
=
(
subjectId
)
=>
{
const
subject
=
subjects
.
value
.
find
(
s
=>
s
.
subject_id
===
subjectId
)
return
subject
?
subject
.
subject_name
:
subjectId
}
const
getTermName
=
(
termId
)
=>
{
const
term
=
terms
.
value
.
find
(
t
=>
t
.
term_id
===
termId
)
return
term
?
term
.
term_name
:
termId
}
const
getTrackedStatus
=
(
flag
)
=>
{
return
flag
===
'
Y
'
?
t
(
'
story.status.tracked
'
)
:
t
(
'
story.status.notTracked
'
)
}
</
script
>
<
template
>
<v-container
fluid
>
<v-row
class=
"mb-4"
align=
"center"
justify=
"space-between"
>
<v-col
cols=
"12"
sm=
"6"
>
<h2
class=
"text-h5"
>
{{
t
(
'
story.title
'
)
}}
</h2>
</v-col>
<v-col
cols=
"12"
sm=
"6"
class=
"text-sm-right text-right"
>
<v-btn
color=
"success"
prepend-icon=
"mdi-plus"
class=
"mr-2"
@
click=
"openCreateDialog"
>
{{
t
(
'
story.button.create
'
)
}}
</v-btn>
<v-btn
color=
"primary"
prepend-icon=
"mdi-refresh"
:loading=
"isLoading"
@
click=
"fetchStories"
>
{{
t
(
'
common.buttons.refresh
'
)
}}
</v-btn>
</v-col>
</v-row>
<!-- 成功消息提示 -->
<v-alert
v-if=
"successMessage"
type=
"success"
class=
"mb-4"
closable
@
click:close=
"successMessage = ''"
>
{{
successMessage
}}
</v-alert>
<!-- 错误消息提示 -->
<v-alert
v-if=
"isError"
type=
"error"
class=
"mb-4"
closable
>
{{
errorMessage
}}
</v-alert>
<v-progress-linear
v-if=
"isLoading"
indeterminate
color=
"primary"
class=
"mb-4"
></v-progress-linear>
<!-- 数据表格 -->
<v-data-table
:headers=
"headers"
:items=
"stories"
item-key=
"story_id"
:items-per-page=
"10"
class=
"elevation-1"
:sort-by=
"[
{ key: 'story_name', order: 'asc' }]"
multi-sort
:items-per-page-text="t('common.table.pagination.itemsPerPage')"
:no-data-text="t('common.table.pagination.noDataText')"
:loading-text="t('common.table.pagination.loadingText')"
:items-per-page-options="[
{ value: 5, title: '5' },
{ value: 10, title: '10' },
{ value: 25, title: '25' },
{ value: 50, title: '50' },
{ value: -1, title: t('common.table.pagination.itemsPerPageAll') }
]"
:hide-default-footer="false"
height="calc(100vh - 360px)"
fixed-header
>
<template
v-slot:
[`
item.student_id
`
]=
"
{ item }">
{{
getStudentName
(
item
.
student_id
)
}}
</
template
>
<
template
v-slot:
[`
item.subject_id
`
]=
"{ item }"
>
{{
getSubjectName
(
item
.
subject_id
)
}}
</
template
>
<
template
v-slot:
[`
item.term_id
`
]=
"{ item }"
>
{{
getTermName
(
item
.
term_id
)
}}
</
template
>
<
template
v-slot:
[`
item.tracked_flag
`
]=
"{ item }"
>
<v-chip
:color=
"item.tracked_flag === 'Y' ? 'success' : 'grey'"
text-color=
"white"
size=
"small"
>
{{
getTrackedStatus
(
item
.
tracked_flag
)
}}
</v-chip>
</
template
>
<
template
v-slot:
[`
item.actions
`
]=
"{ item }"
>
<v-btn
icon=
"mdi-pencil"
size=
"small"
color=
"primary"
variant=
"text"
@
click=
"openEditDialog(item)"
>
</v-btn>
</
template
>
</v-data-table>
</v-container>
<!-- 编辑对话框 -->
<v-dialog
v-model=
"editDialog"
max-width=
"600px"
>
<v-card>
<v-card-title>
{{ t('story.dialog.edit') }}
</v-card-title>
<v-card-text>
<v-alert
v-if=
"saveErrorMessage"
type=
"error"
variant=
"tonal"
closable
class=
"mb-4"
>
{{ saveErrorMessage }}
</v-alert>
<v-form
@
submit.prevent=
"saveStory"
>
<v-container>
<v-row>
<v-col
cols=
"12"
>
<v-text-field
v-model=
"editForm.story_name"
:label=
"t('story.form.storyName') + ' *'"
required
:rules=
"[v => !!v || t('story.validation.storyNameRequired')]"
variant=
"outlined"
></v-text-field>
</v-col>
<v-col
cols=
"12"
>
<v-textarea
v-model=
"editForm.description"
:label=
"t('story.form.description')"
rows=
"3"
variant=
"outlined"
></v-textarea>
</v-col>
<v-col
cols=
"12"
md=
"6"
>
<v-select
v-model=
"editForm.student_id"
:items=
"students"
item-title=
"student_name"
item-value=
"student_id"
:label=
"t('story.form.studentId') + ' *'"
required
:rules=
"[v => !!v || t('story.validation.studentRequired')]"
:loading=
"isLoadingRelatedData"
variant=
"outlined"
></v-select>
</v-col>
<v-col
cols=
"12"
md=
"6"
>
<v-select
v-model=
"editForm.subject_id"
:items=
"subjects"
item-title=
"subject_name"
item-value=
"subject_id"
:label=
"t('story.form.subjectId') + ' *'"
required
:rules=
"[v => !!v || t('story.validation.subjectRequired')]"
:loading=
"isLoadingRelatedData"
variant=
"outlined"
></v-select>
</v-col>
<v-col
cols=
"12"
md=
"6"
>
<v-select
v-model=
"editForm.term_id"
:items=
"terms"
item-title=
"term_name"
item-value=
"term_id"
:label=
"t('story.form.termId') + ' *'"
required
:rules=
"[v => !!v || t('story.validation.termRequired')]"
:loading=
"isLoadingRelatedData"
variant=
"outlined"
></v-select>
</v-col>
<v-col
cols=
"12"
md=
"6"
>
<v-switch
v-model=
"editForm.tracked_flag"
:label=
"t('story.form.trackedFlag')"
color=
"primary"
true-value=
"Y"
false-value=
"N"
inset
></v-switch>
</v-col>
</v-row>
</v-container>
</v-form>
</v-card-text>
<v-card-actions>
<v-btn
color=
"error"
variant=
"outlined"
prepend-icon=
"mdi-delete"
@
click=
"openDeleteDialog(editingStory)"
:disabled=
"isSaving"
>
{{ t('common.buttons.delete') }}
</v-btn>
<v-spacer></v-spacer>
<v-btn
color=
"grey-darken-1"
variant=
"text"
@
click=
"closeEditDialog"
>
{{ t('common.buttons.cancel') }}
</v-btn>
<v-btn
color=
"primary"
variant=
"text"
@
click=
"saveStory"
:loading=
"isSaving"
:disabled=
"isSaving"
>
{{ t('common.buttons.save') }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 新增对话框 -->
<v-dialog
v-model=
"createDialog"
max-width=
"600px"
>
<v-card>
<v-card-title>
{{ t('story.dialog.create') }}
</v-card-title>
<v-card-text>
<v-alert
v-if=
"createErrorMessage"
type=
"error"
variant=
"tonal"
closable
class=
"mb-4"
>
{{ createErrorMessage }}
</v-alert>
<v-form
@
submit.prevent=
"createNewStory"
>
<v-container>
<v-row>
<v-col
cols=
"12"
>
<v-text-field
v-model=
"createForm.story_name"
:label=
"t('story.form.storyName') + ' *'"
required
:rules=
"[v => !!v || t('story.validation.storyNameRequired')]"
variant=
"outlined"
></v-text-field>
</v-col>
<v-col
cols=
"12"
>
<v-textarea
v-model=
"createForm.description"
:label=
"t('story.form.description')"
rows=
"3"
variant=
"outlined"
></v-textarea>
</v-col>
<v-col
cols=
"12"
md=
"6"
>
<v-select
v-model=
"createForm.student_id"
:items=
"students"
item-title=
"student_name"
item-value=
"student_id"
:label=
"t('story.form.studentId') + ' *'"
required
:rules=
"[v => !!v || t('story.validation.studentRequired')]"
:loading=
"isLoadingRelatedData"
variant=
"outlined"
></v-select>
</v-col>
<v-col
cols=
"12"
md=
"6"
>
<v-select
v-model=
"createForm.subject_id"
:items=
"subjects"
item-title=
"subject_name"
item-value=
"subject_id"
:label=
"t('story.form.subjectId') + ' *'"
required
:rules=
"[v => !!v || t('story.validation.subjectRequired')]"
:loading=
"isLoadingRelatedData"
variant=
"outlined"
></v-select>
</v-col>
<v-col
cols=
"12"
md=
"6"
>
<v-select
v-model=
"createForm.term_id"
:items=
"terms"
item-title=
"term_name"
item-value=
"term_id"
:label=
"t('story.form.termId') + ' *'"
required
:rules=
"[v => !!v || t('story.validation.termRequired')]"
:loading=
"isLoadingRelatedData"
variant=
"outlined"
></v-select>
</v-col>
<v-col
cols=
"12"
md=
"6"
>
<v-switch
v-model=
"createForm.tracked_flag"
:label=
"t('story.form.trackedFlag')"
color=
"primary"
true-value=
"Y"
false-value=
"N"
inset
></v-switch>
</v-col>
</v-row>
</v-container>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color=
"grey-darken-1"
variant=
"text"
@
click=
"closeCreateDialog"
>
{{ t('common.buttons.cancel') }}
</v-btn>
<v-btn
color=
"success"
variant=
"text"
@
click=
"createNewStory"
:loading=
"isCreating"
:disabled=
"isCreating"
>
{{ t('common.buttons.create') }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 删除确认对话框 -->
<v-dialog
v-model=
"deleteDialog"
max-width=
"400px"
>
<v-card>
<v-card-title
class=
"text-h5 text-error"
>
<v-icon
icon=
"mdi-alert"
class=
"mr-2"
/>
{{ t('story.deleteConfirm.title') }}
</v-card-title>
<v-card-text>
<div
class=
"text-body-1 mb-4"
>
{{ t('story.deleteConfirm.message') }}
</div>
<div
v-if=
"storyToDelete"
class=
"bg-grey-lighten-4 pa-3 rounded"
>
<div
class=
"font-weight-medium"
>
{{ storyToDelete.story_name }}
</div>
<div
class=
"text-caption text-medium-emphasis"
>
{{ t('story.deleteConfirm.storyInfo', {
name: storyToDelete.story_name
}) }}
</div>
</div>
<div
class=
"text-body-2 text-error mt-4"
>
<v-icon
icon=
"mdi-alert-circle"
size=
"small"
class=
"mr-1"
/>
{{ t('story.deleteConfirm.warning') }}
</div>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color=
"grey-darken-1"
variant=
"text"
@
click=
"closeDeleteDialog"
:disabled=
"isDeleting"
>
{{ t('common.buttons.cancel') }}
</v-btn>
<v-btn
color=
"error"
variant=
"elevated"
prepend-icon=
"mdi-delete"
@
click=
"confirmDelete"
:loading=
"isDeleting"
:disabled=
"isDeleting"
>
{{ t('common.buttons.confirm') + ' ' + t('common.buttons.delete') }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<
style
scoped
>
.v-data-table
{
width
:
100%
;
}
</
style
>
src/views/StudentView.vue
View file @
b8cdc054
...
...
@@ -577,7 +577,7 @@ const saveStudent = async () => {
</div>
</template>
<
template
v-slot:
[`
item.enabled
`
]=
"{ item }"
>
<v-chip
:color=
"item.enabled === 'Y' ? 'success' : 'error'"
size=
"small"
variant=
"flat
"
>
<v-chip
:color=
"item.enabled === 'Y' ? 'success' : 'error'"
text-color=
"white"
size=
"small
"
>
{{
item
.
enabled
===
'
Y
'
?
$t
(
'
common.status.enabled
'
)
:
$t
(
'
common.status.disabled
'
)
}}
</v-chip>
</
template
>
...
...
@@ -714,6 +714,9 @@ const saveStudent = async () => {
v-model=
"editForm.enabled"
:label=
"$t('student.form.enabledStatus')"
color=
"primary"
:true-value=
"true"
:false-value=
"false"
inset
/>
</v-col>
</v-row>
...
...
@@ -871,6 +874,9 @@ const saveStudent = async () => {
v-model=
"createForm.enabled"
:label=
"$t('student.form.enabledStatus') + ' *'"
color=
"primary"
:true-value=
"true"
:false-value=
"false"
inset
/>
</v-col>
</v-row>
...
...
src/views/SubjectView.vue
View file @
b8cdc054
...
...
@@ -390,12 +390,12 @@ const getCalendarColorValue = (colorCode) => {
fixed-header
>
<template
v-slot:
[`
item.enabled_flag
`
]=
"
{ item }">
<v-chip
:color=
"item.enabled_flag === 'Y' ? 'success' : 'error'"
size=
"small"
variant=
"flat
"
>
<v-chip
:color=
"item.enabled_flag === 'Y' ? 'success' : 'error'"
text-color=
"white"
size=
"small
"
>
{{
item
.
enabled_flag
===
'
Y
'
?
$t
(
'
subject.status.enabled
'
)
:
$t
(
'
subject.status.disabled
'
)
}}
</v-chip>
</
template
>
<
template
v-slot:
[`
item.primary_flag
`
]=
"{ item }"
>
<v-chip
:color=
"item.primary_flag === 'Y' ? 'primary' : 'default'"
size=
"small"
variant=
"flat
"
>
<v-chip
:color=
"item.primary_flag === 'Y' ? 'primary' : 'default'"
text-color=
"white"
size=
"small
"
>
{{
item
.
primary_flag
===
'
Y
'
?
$t
(
'
subject.status.primary
'
)
:
$t
(
'
subject.status.notPrimary
'
)
}}
</v-chip>
</
template
>
...
...
@@ -484,6 +484,7 @@ const getCalendarColorValue = (colorCode) => {
color=
"primary"
true-value=
"Y"
false-value=
"N"
inset
/>
</v-col>
<v-col
cols=
"6"
>
...
...
@@ -493,6 +494,7 @@ const getCalendarColorValue = (colorCode) => {
color=
"primary"
true-value=
"Y"
false-value=
"N"
inset
/>
</v-col>
</v-row>
...
...
@@ -596,6 +598,7 @@ const getCalendarColorValue = (colorCode) => {
color=
"primary"
true-value=
"Y"
false-value=
"N"
inset
/>
</v-col>
<v-col
cols=
"6"
>
...
...
@@ -605,6 +608,7 @@ const getCalendarColorValue = (colorCode) => {
color=
"primary"
true-value=
"Y"
false-value=
"N"
inset
/>
</v-col>
</v-row>
...
...
src/views/TaskView.vue
View file @
b8cdc054
...
...
@@ -345,7 +345,7 @@
/>
</v-col>
<v-col
cols=
"12"
md=
"6"
>
<v-text-field
<!--
<v-text-field
v-model="editForm.completion_percent"
:label="$t('task.task.completionPercent') + ' *'"
:placeholder="$t('task.task.completionPercent')"
...
...
@@ -355,7 +355,29 @@
min="0"
max="100"
required
/> -->
<v-slider
v-model=
"editForm.completion_percent"
:label=
"$t('task.task.completionPercent') + ' *'"
:min=
"0"
:max=
"100"
:step=
"1"
thumb-label=
"always"
persistent-hint
:hint=
"`${editForm.completion_percent}%`"
>
<
template
#append
>
<v-text-field
v-model=
"editForm.completion_percent"
type=
"number"
min=
"0"
max=
"100"
hide-details
density=
"compact"
style=
"width: 80px;"
/>
</
template
>
</v-slider>
</v-col>
<v-col
cols=
"12"
md=
"6"
>
<v-text-field
...
...
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