我的成绩支持删除本人记录:新增session/delete接口(仅本人、连带答题明细),历史行增加删除按钮与确认弹窗
This commit is contained in:
5 files changed
+95
-10
No files matched your search
@@ -1431,6 +1431,31 @@ func ApiQuiz(r *gin.RouterGroup) {
|
||||
})
|
||||
})
|
||||
|
||||
// 删除本人的成绩记录(连带答题明细)
|
||||
r.POST("/session/delete", func(ctx *gin.Context) {
|
||||
isAuth, user, data := AuthenticationAuthority(ctx)
|
||||
if !isAuth {
|
||||
ReturnJson(ctx, "userCookieError", nil)
|
||||
return
|
||||
}
|
||||
type FromDelete struct {
|
||||
ID uint `json:"id"`
|
||||
}
|
||||
var from FromDelete
|
||||
if err := decodeJSON(data, &from); err != nil || from.ID == 0 {
|
||||
ReturnJson(ctx, "jsonErr", nil)
|
||||
return
|
||||
}
|
||||
var session TabQuizSession
|
||||
if err := models.DB.First(&session, from.ID).Error; err != nil || session.UserID != user.ID {
|
||||
ReturnJson(ctx, "quiz_session_not_found", nil)
|
||||
return
|
||||
}
|
||||
models.DB.Delete(&TabQuizAnswer{}, "session_id = ?", session.ID)
|
||||
models.DB.Delete(&TabQuizSession{}, session.ID)
|
||||
ReturnJson(ctx, "apiOK", nil)
|
||||
})
|
||||
|
||||
// 错题重做:仅判分,不写入任何数据
|
||||
r.POST("/redo", func(ctx *gin.Context) {
|
||||
isAuth, user, data := AuthenticationAuthority(ctx)
|
||||
|
||||
@@ -53,6 +53,10 @@ export const quizApi = {
|
||||
mySessions(params = {}) {
|
||||
return api.post('/quiz/sessions', params)
|
||||
},
|
||||
/** 删除自己的成绩记录 */
|
||||
deleteSession(id) {
|
||||
return api.post('/quiz/session/delete', { id })
|
||||
},
|
||||
/** 答题回顾详情 */
|
||||
getSession(id) {
|
||||
return api.post('/quiz/session', { id })
|
||||
|
||||
@@ -883,7 +883,10 @@
|
||||
"learn_title": "Study Mode",
|
||||
"learn_empty": "No questions in this bank yet",
|
||||
"loading_more": "Loading...",
|
||||
"all_loaded": "All questions loaded"
|
||||
"all_loaded": "All questions loaded",
|
||||
"delete_record": "Delete",
|
||||
"delete_record_title": "Delete Score Record",
|
||||
"delete_record_confirm": "Delete this score record? This cannot be undone."
|
||||
},
|
||||
"questions": {
|
||||
"title": "Question Bank",
|
||||
|
||||
@@ -884,7 +884,10 @@
|
||||
"learn_title": "学习模式",
|
||||
"learn_empty": "该题库暂无题目",
|
||||
"loading_more": "加载中…",
|
||||
"all_loaded": "已加载全部"
|
||||
"all_loaded": "已加载全部",
|
||||
"delete_record": "删除",
|
||||
"delete_record_title": "删除成绩记录",
|
||||
"delete_record_confirm": "确认删除该次成绩记录?删除后不可恢复。"
|
||||
},
|
||||
"questions": {
|
||||
"title": "题库管理",
|
||||
|
||||
@@ -20,8 +20,11 @@ import {
|
||||
IconChevronsLeft,
|
||||
IconChevronsRight,
|
||||
IconReload,
|
||||
IconTrash,
|
||||
} from '@tabler/icons-vue'
|
||||
|
||||
import ConfirmDialog from '@/components/ConfirmDialog.vue'
|
||||
|
||||
usePageTitle('appname.quiz_bank')
|
||||
const { t, locale } = useI18n()
|
||||
const router = useRouter()
|
||||
@@ -176,6 +179,36 @@ function redoWrong(s) {
|
||||
router.push(`/quiz/redo/${s.id}`)
|
||||
}
|
||||
|
||||
// 删除本人成绩记录
|
||||
const delSessionTarget = ref(null)
|
||||
const confirmDelSession = ref(false)
|
||||
|
||||
function askDeleteSession(s) {
|
||||
delSessionTarget.value = s
|
||||
confirmDelSession.value = true
|
||||
}
|
||||
|
||||
async function doDeleteSession() {
|
||||
if (!delSessionTarget.value) return
|
||||
try {
|
||||
const { errCode } = await quizApi.deleteSession(delSessionTarget.value.id)
|
||||
if (errCode === 0) {
|
||||
toast.success(t('message.delete_success'))
|
||||
confirmDelSession.value = false
|
||||
delSessionTarget.value = null
|
||||
if (sessions.value.length === 1 && historyPage.value > 1) {
|
||||
historyPage.value -= 1
|
||||
}
|
||||
fetchSessions()
|
||||
fetchBoard()
|
||||
} else {
|
||||
toast.error(t('message.server_error'))
|
||||
}
|
||||
} catch {
|
||||
// 拦截器已处理
|
||||
}
|
||||
}
|
||||
|
||||
// ── 排行榜 ──
|
||||
async function fetchBoard() {
|
||||
loadingBoard.value = true
|
||||
@@ -372,14 +405,23 @@ onMounted(() => {
|
||||
<td class="px-6 py-3 text-gray-500 dark:text-gray-400">{{ s.durationSec }} {{ t('quiz.seconds') }}</td>
|
||||
<td class="whitespace-nowrap px-6 py-3 text-gray-500 dark:text-gray-400">{{ formatDate(s.createdAt) }}</td>
|
||||
<td class="px-6 py-3">
|
||||
<button
|
||||
v-if="s.wrongCount > 0"
|
||||
class="inline-flex items-center gap-1 whitespace-nowrap rounded-lg border border-blue-300 px-2.5 py-1 text-xs font-medium text-blue-600 transition-colors hover:bg-blue-50 dark:border-blue-700 dark:text-blue-400 dark:hover:bg-blue-900/20"
|
||||
@click.stop="redoWrong(s)"
|
||||
>
|
||||
<IconReload :size="13" />
|
||||
{{ t('quiz.redo') }}
|
||||
</button>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
v-if="s.wrongCount > 0"
|
||||
class="inline-flex items-center gap-1 whitespace-nowrap rounded-lg border border-blue-300 px-2.5 py-1 text-xs font-medium text-blue-600 transition-colors hover:bg-blue-50 dark:border-blue-700 dark:text-blue-400 dark:hover:bg-blue-900/20"
|
||||
@click.stop="redoWrong(s)"
|
||||
>
|
||||
<IconReload :size="13" />
|
||||
{{ t('quiz.redo') }}
|
||||
</button>
|
||||
<button
|
||||
class="rounded-lg border border-red-300 p-1.5 text-red-500 transition-colors hover:bg-red-50 dark:border-red-700 dark:text-red-400 dark:hover:bg-red-900/20"
|
||||
:title="t('quiz.delete_record')"
|
||||
@click.stop="askDeleteSession(s)"
|
||||
>
|
||||
<IconTrash :size="14" />
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="!loadingHistory && sessions.length === 0">
|
||||
@@ -470,5 +512,13 @@ onMounted(() => {
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ConfirmDialog
|
||||
v-model="confirmDelSession"
|
||||
:title="t('quiz.delete_record_title')"
|
||||
:message="t('quiz.delete_record_confirm')"
|
||||
danger
|
||||
@confirm="doDeleteSession"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user