feat(exam): 优化单词详情展示为单词名称显示
- 新增接口通过单词ID批量获取对应单词名称 - 展示正确单词和错误单词时使用获取的单词名称替代ID - 调整ExamWordsDetailCard组件,新增correctTitles和wrongTitles响应式数据 - 封装fetchTitles方法异步获取单词名称并存入状态 - 修改模板部分渲染,使用单词名称列表动态渲染el-tag标签 - 添加Vocabulary接口相关VO和控制层实现单词名称查询功能 - 新增前端接口调用实现词汇名称数据请求功能
This commit is contained in:
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
7
enlish-vue/src/api/words.js
Normal file
7
enlish-vue/src/api/words.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import axios from "@/axios";
|
||||||
|
|
||||||
|
export function getWordsListByIds(ids) {
|
||||||
|
return axios.post('/vocabulary/list', {
|
||||||
|
ids: ids
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -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) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user