diff --git a/apps/admin/.env.dev b/apps/admin/.env.dev index 0a2ba9c..9716c53 100644 --- a/apps/admin/.env.dev +++ b/apps/admin/.env.dev @@ -5,7 +5,8 @@ # VITE_SERVICE_BASE_URL=http://api.ydzx.qyzhjy.com -VITE_SERVICE_BASE_URL=http://api.ydzx.qyzhjy.com/ +# VITE_SERVICE_BASE_URL=http://api.ydzx.qyzhjy.com/ +VITE_SERVICE_BASE_URL=http://172.16.10.130:5000 VITE_OTHER_SERVICE_BASE_URL= `{ "demo": "http://localhost:9528" diff --git a/apps/admin/src/views/results/results-detail/index.vue b/apps/admin/src/views/results/results-detail/index.vue index 9033335..3463dcc 100644 --- a/apps/admin/src/views/results/results-detail/index.vue +++ b/apps/admin/src/views/results/results-detail/index.vue @@ -270,6 +270,42 @@ async function fetchCurrentQuestion(shouldSync = true) { } } +const blobSizeCache = new Map() +const objectUrlCache = new Map() + +async function checkAndUpdateImage(uniqueId: string, originalUrl: string, item: any) { + try { + const timestamp = Date.now() + const separator = originalUrl.includes('?') ? '&' : '?' + const fetchUrl = `${originalUrl}${separator}_t=${timestamp}` + + const response = await fetch(fetchUrl) + if (!response.ok) + return + const blob = await response.blob() + + const prevSize = blobSizeCache.get(uniqueId) + if (prevSize === blob.size) { + return // 内容大小未变,认为图片没变,不更新 src,避免闪烁 + } + + blobSizeCache.set(uniqueId, blob.size) + const objectUrl = URL.createObjectURL(blob) + + const oldUrl = objectUrlCache.get(uniqueId) + if (oldUrl) { + URL.revokeObjectURL(oldUrl) + } + objectUrlCache.set(uniqueId, objectUrl) + + // 替换为本地 Blob URL,由于数据已在内存中,更新时不会闪烁 + item.AnswerValuePicture = objectUrl + } + catch (e) { + console.error('Silent image update failed:', e) + } +} + /** * 获取答题卡数据 */ @@ -371,13 +407,22 @@ async function fetchAnswerData() { // 强制更新图片 URL,防止缓存 // 优先使用 ans 中的图片,如果没有则回退到 answerData 中的图片 const imageUrl = ans.AnswerValuePicture || (parsedAnswers.length === 1 && parsedAnswers[0] === ans ? answerData.UserAnswerPicture : '') + + const uniqueId = `${answerData.TeamId}-${ans.Guid}` let finalImageUrl = imageUrl + + if (imageUrl) { + if (oldAns && oldAns._originalUrl === imageUrl) { + finalImageUrl = oldAns.AnswerValuePicture + } + else { + finalImageUrl = imageUrl + } + } + const cleanBaseUrl = '' const timestamp = new Date().getTime() - finalImageUrl = `${finalImageUrl}?_t=${timestamp}` - console.log(finalImageUrl, 'finalImageUrl') - // 判断是否手动干预: // 1. 本地存储有状态 (hasStoredStatus) 或有分数 (hasStoredScore) // 2. 内存中有手动标记 (oldAns._isManual) @@ -407,6 +452,7 @@ async function fetchAnswerData() { QuestionDetailID: answerData.QuestionDetaiID, // [新增] 注意后端字段拼写 AnswerText: ans.AnswerText, AnswerValuePicture: finalImageUrl, + _originalUrl: imageUrl, _baseImgUrl: cleanBaseUrl, _t: timestamp, // 保存时间戳 Score: finalScore, @@ -415,17 +461,21 @@ async function fetchAnswerData() { _aiStatus: aiStatus, _aiScore: aiScore, } + if (imageUrl) { + checkAndUpdateImage(uniqueId, imageUrl, resultItem) + } return resultItem } // 如果没有手动编辑 - return { + const resultItem = { ...ans, TeamId: answerData.TeamId, // [新增] 注入 TeamId QuestionId: answerData.QuestionId, // [新增] QuestionDetailID: answerData.QuestionDetaiID, // [新增] 注意后端字段拼写 AnswerText: ans.AnswerText, AnswerValuePicture: finalImageUrl, + _originalUrl: imageUrl, _baseImgUrl: cleanBaseUrl, _t: timestamp, // 保存时间戳 Status: ans.Status, @@ -434,6 +484,10 @@ async function fetchAnswerData() { Score: null, // 默认 null _isManual: false, } + if (imageUrl) { + checkAndUpdateImage(uniqueId, imageUrl, resultItem) + } + return resultItem }) return { diff --git a/apps/admin/src/views/user/analysis/index.vue b/apps/admin/src/views/user/analysis/index.vue index ad4255b..a8f7c2e 100644 --- a/apps/admin/src/views/user/analysis/index.vue +++ b/apps/admin/src/views/user/analysis/index.vue @@ -117,6 +117,42 @@ async function checkNextStep() { } } +const blobSizeCache = new Map() +const objectUrlCache = new Map() + +async function checkAndUpdateImage(uniqueId: string, originalUrl: string, team: any) { + try { + const timestamp = Date.now() + const separator = originalUrl.includes('?') ? '&' : '?' + const fetchUrl = `${originalUrl}${separator}_t=${timestamp}` + + const response = await fetch(fetchUrl) + if (!response.ok) + return + const blob = await response.blob() + + const prevSize = blobSizeCache.get(uniqueId) + if (prevSize === blob.size) { + return // 内容大小未变,认为图片没变,不更新 src,避免闪烁 + } + + blobSizeCache.set(uniqueId, blob.size) + const objectUrl = URL.createObjectURL(blob) + + const oldUrl = objectUrlCache.get(uniqueId) + if (oldUrl) { + URL.revokeObjectURL(oldUrl) + } + objectUrlCache.set(uniqueId, objectUrl) + + // 替换为本地 Blob URL,由于数据已在内存中,更新时不会闪烁 + team.UserAnswerPicture = objectUrl + } + catch (e) { + console.error('Silent image update failed:', e) + } +} + /** 获取游戏现场统计结果 */ async function fetchGetGameStatisticsData() { if (store.currentQuestionInfo && store.currentQuestionDetail) { @@ -129,12 +165,25 @@ async function fetchGetGameStatisticsData() { const { data } = await fetchGetGameStatistics(_params) const rawTeams = (data?.data as any[]) || [] - // 为每个队伍的图片 URL 添加时间戳,防止缓存 teams.value = rawTeams.map((team) => { + const uniqueId = team.TeamID || team.TeamName || Math.random().toString() + team._uniqueId = uniqueId + if (team.UserAnswerPicture) { - const timestamp = Date.now() - const separator = team.UserAnswerPicture.includes('?') ? '&' : '?' - team.UserAnswerPicture = `${team.UserAnswerPicture}${separator}_t=${timestamp}` + const originalUrl = team.UserAnswerPicture + const oldTeam = teams.value.find(t => t._uniqueId === uniqueId) + + if (oldTeam && oldTeam.UserAnswerPicture && oldTeam._originalUrl === originalUrl) { + // 保持原有的 URL,避免每次轮询都刷新 + team.UserAnswerPicture = oldTeam.UserAnswerPicture + } + else { + team.UserAnswerPicture = originalUrl + } + team._originalUrl = originalUrl + + // 后台静默获取最新图片并对比 + checkAndUpdateImage(uniqueId, originalUrl, team) } return team }) @@ -164,7 +213,7 @@ onBeforeUnmount(() => { back-btn-text="返回" :next-btn-text="isCompleted ? '已完成,返选组' : '下一题'" btn-theme="light" @back="handleBack" @next="handleNext" > -
+
@@ -177,15 +226,17 @@ onBeforeUnmount(() => { class="flex flex-col border-2 border-gray-200 rounded-xl bg-white/90 p-4 shadow-sm" >
- {{ team.TeamName }} - - 评委评分 {{ team.ResultPotins }} - - - AI评分 {{ team.Points }} - + {{ team.TeamName }} +
+ + 评委评分 {{ team.ResultPotins }} + + + AI评分 {{ team.Points }} + +
diff --git a/apps/admin/src/views/user/game/modules/LineCharts.vue b/apps/admin/src/views/user/game/modules/LineCharts.vue index 4002717..4497a7d 100644 --- a/apps/admin/src/views/user/game/modules/LineCharts.vue +++ b/apps/admin/src/views/user/game/modules/LineCharts.vue @@ -41,9 +41,9 @@ function buildOption(list: BarDatum[]): ECOption { return { // 启用动画,让柱子变化更平滑 - animation: true, + animation: false, animationDuration: 800, - animationEasing: 'cubicOut', + animationEasing: 'linear', // 动画缓动函数,平滑过渡,cubicOut|linear|quadOut|quadIn tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' },