feat(exam): 优化单词详情展示为单词名称显示

- 新增接口通过单词ID批量获取对应单词名称
- 展示正确单词和错误单词时使用获取的单词名称替代ID
- 调整ExamWordsDetailCard组件,新增correctTitles和wrongTitles响应式数据
- 封装fetchTitles方法异步获取单词名称并存入状态
- 修改模板部分渲染,使用单词名称列表动态渲染el-tag标签
- 添加Vocabulary接口相关VO和控制层实现单词名称查询功能
- 新增前端接口调用实现词汇名称数据请求功能
This commit is contained in:
lbw
2025-12-14 16:01:31 +08:00
parent 1ace63cbe0
commit 6eb46f606e
5 changed files with 109 additions and 6 deletions

View File

@@ -0,0 +1,33 @@
package com.yinlihupo.enlish.service.controller;
import com.yinlihupo.enlish.service.domain.dataobject.VocabularyBankDO;
import com.yinlihupo.enlish.service.model.vo.vocabulary.FindWordTitleReqVO;
import com.yinlihupo.enlish.service.model.vo.vocabulary.FindWordTitleRspVO;
import com.yinlihupo.enlish.service.service.VocabularyService;
import com.yinlihupo.framework.biz.operationlog.aspect.ApiOperationLog;
import com.yinlihupo.framework.common.response.Response;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RequestMapping("/vocabulary/")
@RestController
public class VocabularyController {
@Resource
private VocabularyService vocabularyService;
@PostMapping("/list")
@ApiOperationLog(description = "查询单词名称")
public Response<FindWordTitleRspVO> findWordTitle(@RequestBody FindWordTitleReqVO vo) {
List<VocabularyBankDO> vocabularyBankDOListById = vocabularyService.findVocabularyBankDOListById(vo.getIds());
FindWordTitleRspVO findWordTitleRspVO = FindWordTitleRspVO.builder()
.wordTitles(vocabularyBankDOListById.stream().map(VocabularyBankDO::getWord).toList())
.build();
return Response.success(findWordTitleRspVO);
}
}

View File

@@ -0,0 +1,17 @@
package com.yinlihupo.enlish.service.model.vo.vocabulary;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class FindWordTitleReqVO {
private List<Integer> ids;
}

View File

@@ -0,0 +1,17 @@
package com.yinlihupo.enlish.service.model.vo.vocabulary;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class FindWordTitleRspVO {
private List<String> wordTitles;
}

View File

@@ -0,0 +1,7 @@
import axios from "@/axios";
export function getWordsListByIds(ids) {
return axios.post('/vocabulary/list', {
ids: ids
})
}

View File

@@ -39,10 +39,10 @@
</template> </template>
<el-scrollbar style="max-height: 240px"> <el-scrollbar style="max-height: 240px">
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
<template v-if="(detail?.correctWordIds?.length ?? 0) > 0"> <template v-if="(correctTitles.length ?? 0) > 0">
<el-tag v-for="id in (detail?.correctWordIds || [])" :key="'c-' + id" type="success" <el-tag v-for="(title, idx) in correctTitles" :key="'c-' + idx" type="success"
effect="plain"> effect="plain">
{{ id }} {{ title }}
</el-tag> </el-tag>
</template> </template>
<div v-else class="text-gray-500">暂无数据</div> <div v-else class="text-gray-500">暂无数据</div>
@@ -56,10 +56,10 @@
</template> </template>
<el-scrollbar style="max-height: 240px"> <el-scrollbar style="max-height: 240px">
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
<template v-if="(detail?.wrongWordIds?.length ?? 0) > 0"> <template v-if="(wrongTitles.length ?? 0) > 0">
<el-tag v-for="id in (detail?.wrongWordIds || [])" :key="'w-' + id" type="danger" <el-tag v-for="(title, idx) in wrongTitles" :key="'w-' + idx" type="danger"
effect="plain"> effect="plain">
{{ id }} {{ title }}
</el-tag> </el-tag>
</template> </template>
<div v-else class="text-gray-500">暂无数据</div> <div v-else class="text-gray-500">暂无数据</div>
@@ -76,6 +76,7 @@
<script setup> <script setup>
import { ref, computed, watch } from 'vue' import { ref, computed, watch } from 'vue'
import { getExamWordsDetailResult } from '@/api/exam' import { getExamWordsDetailResult } from '@/api/exam'
import { getWordsListByIds } from '@/api/words'
const props = defineProps({ const props = defineProps({
modelValue: { type: Boolean, default: false }, modelValue: { type: Boolean, default: false },
@@ -91,6 +92,8 @@ const visible = computed({
const loading = ref(false) const loading = ref(false)
const detail = ref(null) const detail = ref(null)
const activeNames = ref(['correct', 'wrong']) const activeNames = ref(['correct', 'wrong'])
const correctTitles = ref([])
const wrongTitles = ref([])
async function fetchDetail() { async function fetchDetail() {
if (!props.id && props.id !== 0) return if (!props.id && props.id !== 0) return
@@ -99,11 +102,37 @@ async function fetchDetail() {
const res = await getExamWordsDetailResult(Number(props.id)) const res = await getExamWordsDetailResult(Number(props.id))
const d = res?.data?.data ?? null const d = res?.data?.data ?? null
detail.value = d detail.value = d
await fetchTitles()
} finally { } finally {
loading.value = false loading.value = false
} }
} }
async function fetchTitles() {
correctTitles.value = []
wrongTitles.value = []
const cIds = detail.value?.correctWordIds || []
const wIds = detail.value?.wrongWordIds || []
const tasks = []
if (cIds.length > 0) {
tasks.push(
getWordsListByIds(cIds).then(res => {
correctTitles.value = res?.data?.data?.wordTitles || []
})
)
}
if (wIds.length > 0) {
tasks.push(
getWordsListByIds(wIds).then(res => {
wrongTitles.value = res?.data?.data?.wordTitles || []
})
)
}
if (tasks.length > 0) {
await Promise.all(tasks)
}
}
watch( watch(
() => props.modelValue, () => props.modelValue,
(v) => { (v) => {