rating when ai is steaming
This commit is contained in:
@@ -20,6 +20,7 @@ import type {
|
|||||||
ShowcaseSubmissionLookupOut,
|
ShowcaseSubmissionLookupOut,
|
||||||
ShowcaseDetail,
|
ShowcaseDetail,
|
||||||
PromptRound,
|
PromptRound,
|
||||||
|
RandomRatingItem,
|
||||||
} from "./utils/type"
|
} from "./utils/type"
|
||||||
import { BASE_URL, STORAGE_KEY } from "./utils/const"
|
import { BASE_URL, STORAGE_KEY } from "./utils/const"
|
||||||
|
|
||||||
@@ -237,6 +238,13 @@ export const Submission = {
|
|||||||
return res.data
|
return res.data
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async randomForRating(excludeId?: string) {
|
||||||
|
const res = await http.get("/submission/random-for-rating/", {
|
||||||
|
params: excludeId ? { exclude_id: excludeId } : {},
|
||||||
|
})
|
||||||
|
return res.data as RandomRatingItem | null
|
||||||
|
},
|
||||||
|
|
||||||
async updateFlag(id: string, flag: FlagType) {
|
async updateFlag(id: string, flag: FlagType) {
|
||||||
const res = await http.put(`/submission/${id}/flag`, { flag })
|
const res = await http.put(`/submission/${id}/flag`, { flag })
|
||||||
return res.data
|
return res.data
|
||||||
|
|||||||
135
src/components/ai/RandomRatingModal.vue
Normal file
135
src/components/ai/RandomRatingModal.vue
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
<template>
|
||||||
|
<n-modal
|
||||||
|
preset="card"
|
||||||
|
:show="show"
|
||||||
|
title="等待 AI 回复时,给同学的作品打个分吧"
|
||||||
|
style="width: 480px"
|
||||||
|
@update:show="onUpdateShow"
|
||||||
|
>
|
||||||
|
<n-text
|
||||||
|
v-if="current"
|
||||||
|
depth="3"
|
||||||
|
style="font-size: 12px; display: block; margin-bottom: 8px"
|
||||||
|
>
|
||||||
|
{{ current.task_title }} · 提交者 {{ current.username }}
|
||||||
|
</n-text>
|
||||||
|
<div class="preview-wrapper">
|
||||||
|
<iframe class="preview-iframe" :srcdoc="previewContent"></iframe>
|
||||||
|
</div>
|
||||||
|
<div class="rate-row">
|
||||||
|
<n-rate
|
||||||
|
v-if="current"
|
||||||
|
:key="current.submission_id"
|
||||||
|
:size="32"
|
||||||
|
@update:value="onRate"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<n-text
|
||||||
|
depth="3"
|
||||||
|
style="font-size: 11px; display: block; text-align: center; margin-top: 8px"
|
||||||
|
>
|
||||||
|
打分后自动换下一个作品;AI 回复完成后弹窗会自动关闭
|
||||||
|
</n-text>
|
||||||
|
</n-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch, onUnmounted } from "vue"
|
||||||
|
import { useMessage } from "naive-ui"
|
||||||
|
import { Submission } from "../../api"
|
||||||
|
import { streaming } from "../../store/prompt"
|
||||||
|
import { buildPreviewDocument } from "../../utils/previewDocument"
|
||||||
|
import type { RandomRatingItem } from "../../utils/type"
|
||||||
|
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
const show = ref(false)
|
||||||
|
const current = ref<RandomRatingItem | null>(null)
|
||||||
|
const previewContent = ref("")
|
||||||
|
let reshowTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
|
function clearReshowTimer() {
|
||||||
|
if (reshowTimer !== null) {
|
||||||
|
clearTimeout(reshowTimer)
|
||||||
|
reshowTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchAndShow() {
|
||||||
|
try {
|
||||||
|
const item = await Submission.randomForRating(current.value?.submission_id)
|
||||||
|
if (item) {
|
||||||
|
current.value = item
|
||||||
|
previewContent.value = buildPreviewDocument({
|
||||||
|
html: item.html ?? "",
|
||||||
|
css: item.css ?? "",
|
||||||
|
js: item.js ?? "",
|
||||||
|
})
|
||||||
|
show.value = true
|
||||||
|
} else {
|
||||||
|
show.value = false
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
message.error(err.response?.data?.detail ?? "获取作品失败,请重试")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onRate(score: number) {
|
||||||
|
if (!current.value) return
|
||||||
|
try {
|
||||||
|
await Submission.updateScore(current.value.submission_id, score)
|
||||||
|
message.success("感谢评分!")
|
||||||
|
await fetchAndShow()
|
||||||
|
} catch (err: any) {
|
||||||
|
message.error(err.response?.data?.detail ?? "打分失败,请重试")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUpdateShow(value: boolean) {
|
||||||
|
show.value = value
|
||||||
|
if (!value) {
|
||||||
|
clearReshowTimer()
|
||||||
|
if (streaming.value) {
|
||||||
|
reshowTimer = setTimeout(fetchAndShow, 30_000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
streaming,
|
||||||
|
(val) => {
|
||||||
|
clearReshowTimer()
|
||||||
|
if (val) {
|
||||||
|
fetchAndShow()
|
||||||
|
} else {
|
||||||
|
show.value = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearReshowTimer()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.preview-wrapper {
|
||||||
|
height: 220px;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-iframe {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rate-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -95,6 +95,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<TaskStatsModal v-model:show="showStats" :task-id="taskId" />
|
<TaskStatsModal v-model:show="showStats" :task-id="taskId" />
|
||||||
|
<RandomRatingModal v-if="authed" />
|
||||||
<n-modal
|
<n-modal
|
||||||
v-model:show="showAssets"
|
v-model:show="showAssets"
|
||||||
preset="card"
|
preset="card"
|
||||||
@@ -144,6 +145,7 @@ import ExternalAIPanel from "../components/ai/ExternalAIPanel.vue"
|
|||||||
import PromptHistoryPanel from "../components/ai/PromptHistoryPanel.vue"
|
import PromptHistoryPanel from "../components/ai/PromptHistoryPanel.vue"
|
||||||
import Preview from "../components/editor/Preview.vue"
|
import Preview from "../components/editor/Preview.vue"
|
||||||
import TaskStatsModal from "../components/task/TaskStatsModal.vue"
|
import TaskStatsModal from "../components/task/TaskStatsModal.vue"
|
||||||
|
import RandomRatingModal from "../components/ai/RandomRatingModal.vue"
|
||||||
import { Challenge, Submission, TaskAssets } from "../api"
|
import { Challenge, Submission, TaskAssets } from "../api"
|
||||||
import type { TaskAsset } from "../utils/type"
|
import type { TaskAsset } from "../utils/type"
|
||||||
import { html, css, js } from "../store/editors"
|
import { html, css, js } from "../store/editors"
|
||||||
|
|||||||
@@ -335,3 +335,14 @@ export interface PromptRound {
|
|||||||
css: string | null
|
css: string | null
|
||||||
js: string | null
|
js: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RandomRatingItem {
|
||||||
|
submission_id: string
|
||||||
|
username: string
|
||||||
|
task_title: string
|
||||||
|
task_display: number
|
||||||
|
task_type: TASK_TYPE
|
||||||
|
html: string | null
|
||||||
|
css: string | null
|
||||||
|
js: string | null
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user