feat: 添加选项题
This commit is contained in:
@@ -3,19 +3,16 @@ import {onMounted, watch} from "vue";
|
||||
import {BaseState, useBaseStore} from "@/stores/base.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import Backgorund from "@/pages/pc/components/Backgorund.vue";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
import * as localforage from "localforage";
|
||||
import SettingDialog from "@/pages/pc/components/dialog/SettingDialog.vue";
|
||||
import ArticleContentDialog from "@/pages/pc/components/dialog/ArticleContentDialog.vue";
|
||||
import CollectNotice from "@/pages/pc/components/CollectNotice.vue";
|
||||
import {SAVE_DICT_KEY, SAVE_SETTING_KEY} from "@/utils/const.ts";
|
||||
import {isMobile, shakeCommonDict} from "@/utils";
|
||||
import router, {routes} from "@/router.ts";
|
||||
|
||||
import {useRoute} from "vue-router";
|
||||
import {splitEnArticle} from "@/hooks/article.ts";
|
||||
|
||||
const store = useBaseStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
@@ -81,7 +78,6 @@ watch(() => route.path, (to, from) => {
|
||||
</transition>
|
||||
</router-view>
|
||||
<CollectNotice/>
|
||||
<ArticleContentDialog/>
|
||||
<SettingDialog/>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -143,6 +143,12 @@ html.dark {
|
||||
.anim {
|
||||
transition: background var(--anim-time), color var(--anim-time), border var(--anim-time);
|
||||
}
|
||||
.en-article-family {
|
||||
font-family: var(--en-article-family);
|
||||
}
|
||||
.font-family {
|
||||
font-family: var(--font-family);
|
||||
}
|
||||
|
||||
html, body {
|
||||
//font-size: 1px;
|
||||
@@ -412,25 +418,6 @@ footer {
|
||||
}
|
||||
}
|
||||
|
||||
&.border {
|
||||
&.active {
|
||||
.item-title {
|
||||
border-bottom: 2px solid gray !important;
|
||||
}
|
||||
}
|
||||
|
||||
.item-title {
|
||||
transition: all .3s;
|
||||
cursor: pointer;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.item-title {
|
||||
border-bottom: 2px solid gray !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-title {
|
||||
display: flex;
|
||||
|
||||
99
src/pages/pc/components/QuestionForm.vue
Normal file
99
src/pages/pc/components/QuestionForm.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div class="question-form en-article-family">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="font-bold">Multiple choice questions 选择题</div>
|
||||
<div v-if="false">
|
||||
<button
|
||||
v-if="!started"
|
||||
class="bg-blue-600 text-white px-4 py-1 rounded"
|
||||
@click="startExam"
|
||||
>开始
|
||||
</button>
|
||||
<span v-if="started" class="text-red-600 font-semibold font-family">
|
||||
倒计时:{{ timeLeft }} 秒
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form @submit.prevent>
|
||||
<QuestionItem
|
||||
v-for="(q, i) in questions"
|
||||
:key="i"
|
||||
ref="questionRefs1"
|
||||
:question-index="i + 1"
|
||||
:stem="q.stem"
|
||||
:options="q.options"
|
||||
:correct-answer="q.correctAnswer"
|
||||
:explanation="q.explanation"
|
||||
:immediate-feedback="props.immediateFeedback"
|
||||
:randomize="props.randomize"
|
||||
@answered="onAnswered"
|
||||
/>
|
||||
</form>
|
||||
|
||||
<div class="flex-center items-center gap-2 mt-10">
|
||||
<button
|
||||
class="bg-green-600 text-white px-6 py-2 rounded"
|
||||
@click="submitAll"
|
||||
>提交试卷
|
||||
</button>
|
||||
<span class="text-xl">浅红:错误 深红:未选 绿:正确</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, useTemplateRef} from 'vue'
|
||||
import QuestionItem from './QuestionItem.vue'
|
||||
|
||||
interface IProps {
|
||||
questions: Array,
|
||||
duration: Number,
|
||||
immediateFeedback: Boolean,
|
||||
randomize: Boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
questions: [],
|
||||
duration: 300,
|
||||
immediateFeedback: false,
|
||||
randomize: false
|
||||
})
|
||||
|
||||
const questionRefs = useTemplateRef('questionRefs1')
|
||||
const started = ref(false)
|
||||
const timeLeft = ref(props.duration || 300)
|
||||
let timer = null
|
||||
|
||||
const startExam = () => {
|
||||
started.value = true
|
||||
timeLeft.value = props.duration || 300
|
||||
timer = setInterval(() => {
|
||||
timeLeft.value--
|
||||
if (timeLeft.value <= 0) {
|
||||
clearInterval(timer)
|
||||
submitAll()
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const onAnswered = (res) => {
|
||||
console.log('Answered:', res)
|
||||
// 可收集中间过程(非必须)
|
||||
}
|
||||
|
||||
const submitAll = () => {
|
||||
console.log(questionRefs)
|
||||
questionRefs.value.forEach((q) => q.submit())
|
||||
const results = questionRefs.value.map((q) => q.getResult())
|
||||
const correctCount = results.filter(r => r.isCorrect).length
|
||||
const wrongCount = results.length - correctCount
|
||||
|
||||
console.log('最终结果:', results)
|
||||
ElMessage({message: `共 ${results.length} 题,答对 ${correctCount},答错 ${wrongCount}`})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
194
src/pages/pc/components/QuestionItem.vue
Normal file
194
src/pages/pc/components/QuestionItem.vue
Normal file
@@ -0,0 +1,194 @@
|
||||
<template>
|
||||
<div ref="container" class="question-item mb-4">
|
||||
<div class="mb-1 "
|
||||
:class="noChoseClass"
|
||||
><span class="font-family">{{ questionIndex }}</span>. {{ stem }}
|
||||
</div>
|
||||
<div
|
||||
class="grid gap-1"
|
||||
:class="layoutClass"
|
||||
>
|
||||
<label
|
||||
v-for="(opt, i) in shuffledOptions"
|
||||
:key="i"
|
||||
class="option border rounded cursor-pointer hover:bg-gray-300"
|
||||
:class="feedbackClass(i)"
|
||||
>
|
||||
<input
|
||||
:type="isMultiple ? 'checkbox' : 'radio'"
|
||||
:name="`question-${questionIndex}`"
|
||||
class="mr-2"
|
||||
:value="opt"
|
||||
v-model="userSelection"
|
||||
@change="onSelect"
|
||||
/>
|
||||
<span ref="optionRefs">(<span class="italic">{{ ['a', 'b', 'c', 'd'][i] }}</span>) {{ opt }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div v-if="explanation && isSubmitted" class="mt-2 text-xl text-gray-600">
|
||||
解析:{{ explanation }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, computed, watch, onMounted, nextTick} from 'vue'
|
||||
import {shuffle} from "lodash-es";
|
||||
|
||||
const props = defineProps({
|
||||
stem: String,
|
||||
options: Array,
|
||||
correctAnswer: Array, // ['a', 'b']
|
||||
explanation: String,
|
||||
immediateFeedback: Boolean,
|
||||
questionIndex: Number,
|
||||
randomize: Boolean
|
||||
})
|
||||
|
||||
const emit = defineEmits(['answered'])
|
||||
|
||||
// 将选项打乱并映射回原始下标
|
||||
const originalOptions = props.options
|
||||
const shuffledOptions = ref([])
|
||||
const answerMap = ref([]) // 映射 shuffled[i] 对应原始 index(用于判分)
|
||||
|
||||
const isMultiple = computed(() => props.correctAnswer.length > 1)
|
||||
const userSelection = ref(isMultiple.value ? [] : '')
|
||||
const isSubmitted = ref(false)
|
||||
const isCorrect = ref(null)
|
||||
|
||||
// 初始化打乱选项
|
||||
const initOptions = () => {
|
||||
const indices = originalOptions.map((_, i) => i)
|
||||
const shuffledIndices = props.randomize ? shuffle(indices) : indices
|
||||
shuffledOptions.value = shuffledIndices.map(i => originalOptions[i])
|
||||
answerMap.value = shuffledIndices
|
||||
}
|
||||
|
||||
initOptions()
|
||||
|
||||
const getLetter = (index) => ['a', 'b', 'c', 'd'][index]
|
||||
const getOriginalLetter = (shuffledIndex) => getLetter(answerMap.value[shuffledIndex])
|
||||
|
||||
const onSelect = () => {
|
||||
if (props.immediateFeedback) submit()
|
||||
emitAnswer()
|
||||
}
|
||||
|
||||
const emitAnswer = () => {
|
||||
const selectedLetters = isMultiple.value
|
||||
? userSelection.value.map(val => getOriginalLetter(shuffledOptions.value.indexOf(val)))
|
||||
: [getOriginalLetter(shuffledOptions.value.indexOf(userSelection.value))]
|
||||
|
||||
const isAnswerCorrect =
|
||||
selectedLetters.sort().join() === props.correctAnswer.sort().join()
|
||||
|
||||
emit('answered', {
|
||||
index: props.questionIndex,
|
||||
selected: selectedLetters,
|
||||
isCorrect: isAnswerCorrect
|
||||
})
|
||||
}
|
||||
|
||||
const submit = () => {
|
||||
isSubmitted.value = true
|
||||
const selectedLetters = isMultiple.value
|
||||
? userSelection.value.map(val => getOriginalLetter(shuffledOptions.value.indexOf(val)))
|
||||
: [getOriginalLetter(shuffledOptions.value.indexOf(userSelection.value))]
|
||||
isCorrect.value =
|
||||
selectedLetters.sort().join() === props.correctAnswer.sort().join()
|
||||
}
|
||||
|
||||
const feedbackClass = (i) => {
|
||||
if (!isSubmitted.value) return ''
|
||||
const selected = isMultiple.value
|
||||
? userSelection.value.includes(shuffledOptions.value[i])
|
||||
: userSelection.value === shuffledOptions.value[i]
|
||||
const correct = props.correctAnswer.includes(getOriginalLetter(i))
|
||||
if (correct) return 'bg-green-200'
|
||||
if (selected && !correct) return 'bg-red-200'
|
||||
return ''
|
||||
}
|
||||
const noChoseClass = computed(() => {
|
||||
if (!isSubmitted.value) return ''
|
||||
const selected = isMultiple.value
|
||||
? userSelection.value.length
|
||||
: userSelection.value
|
||||
return !selected && 'bg-red-400'
|
||||
})
|
||||
|
||||
// 父组件调用此方法统一评分
|
||||
defineExpose({
|
||||
submit,
|
||||
getResult: () => {
|
||||
const selectedLetters = isMultiple.value
|
||||
? userSelection.value.map(val => getOriginalLetter(shuffledOptions.value.indexOf(val)))
|
||||
: [getOriginalLetter(shuffledOptions.value.indexOf(userSelection.value))]
|
||||
return {
|
||||
index: props.questionIndex,
|
||||
selected: selectedLetters,
|
||||
isCorrect: selectedLetters.sort().join() === props.correctAnswer.sort().join()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const optionRefs = ref([])
|
||||
const container = ref(null)
|
||||
const layoutClass = ref('')
|
||||
|
||||
const calculateLayout = () => {
|
||||
if (!container.value || optionRefs.value.length === 0) return
|
||||
|
||||
const containerWidth = container.value.clientWidth
|
||||
const widths = optionRefs.value.map(el => el.getBoundingClientRect().width)
|
||||
|
||||
const totalWidth = widths.reduce((sum, w) => sum + w, 0)
|
||||
// console.log(widths,totalWidth)
|
||||
|
||||
// 如果任意选项宽度超过容器一半
|
||||
if (widths.some(w => w > containerWidth / 2)) {
|
||||
layoutClass.value = 'grid-cols-1'
|
||||
return
|
||||
}
|
||||
|
||||
// 如果所有选项都可以在一行内放下
|
||||
if (totalWidth + 80 * (widths.length - 1) <= containerWidth) {
|
||||
layoutClass.value = 'grid-cols-4'
|
||||
return
|
||||
}
|
||||
|
||||
// 否则 2 列
|
||||
layoutClass.value = 'grid-cols-2'
|
||||
}
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
calculateLayout()
|
||||
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
calculateLayout()
|
||||
})
|
||||
|
||||
resizeObserver.observe(container.value)
|
||||
})
|
||||
|
||||
watch(() => props.options, async () => {
|
||||
await nextTick()
|
||||
calculateLayout()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.option {
|
||||
white-space: normal;
|
||||
text-overflow: unset;
|
||||
overflow: visible;
|
||||
word-break: keep-all;
|
||||
padding: 5px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
</style>
|
||||
@@ -1,97 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import Dialog from "@/pages/pc/components/dialog/Dialog.vue";
|
||||
|
||||
import {onMounted, onUnmounted, watch} from "vue";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import WordList from "@/pages/pc/components/list/WordList.vue";
|
||||
import {Article, DefaultArticle} from "@/types.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import Empty from "@/components/Empty.vue";
|
||||
import {getTranslateText} from "@/hooks/article.ts";
|
||||
|
||||
let show = $ref(false)
|
||||
let loading = $ref(false)
|
||||
const runtimeStore = useRuntimeStore()
|
||||
let article: Article = $ref(cloneDeep(DefaultArticle))
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.openArticleContentModal, (val: any) => {
|
||||
show = true
|
||||
article = cloneDeep(val)
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.openArticleContentModal)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog
|
||||
:header="false"
|
||||
v-model="show">
|
||||
<div class="content">
|
||||
<div class="article-content">
|
||||
<div class="title">
|
||||
<div>{{ article.title }}</div>
|
||||
</div>
|
||||
<div class="text" v-if="article.text">
|
||||
<div class="sentence" v-for="t in article.text.split('\n\n')">{{ t }}</div>
|
||||
</div>
|
||||
<Empty v-else/>
|
||||
</div>
|
||||
<div class="article-content">
|
||||
<div class="title">
|
||||
<div>{{ article.titleTranslate }}</div>
|
||||
</div>
|
||||
<div class="text" v-if="getTranslateText(article).length">
|
||||
<div class="sentence" v-for="t in getTranslateText(article)">{{ t }}</div>
|
||||
</div>
|
||||
<Empty v-else/>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/assets/css/style";
|
||||
|
||||
.content {
|
||||
width: 70vw;
|
||||
height: 75vh;
|
||||
display: flex;
|
||||
gap: var(--space);
|
||||
padding: var(--space);
|
||||
color: var(--color-font-1);
|
||||
|
||||
.article-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
font-size: 1.2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
margin-bottom: var(--space);
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.text {
|
||||
text-indent: 1.5em;
|
||||
line-height: 2rem;
|
||||
overflow: auto;
|
||||
padding-right: .6rem;
|
||||
padding-bottom: 3rem;
|
||||
|
||||
.sentence {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -59,7 +59,7 @@ defineExpose({scrollToBottom, scrollToItem})
|
||||
<slot name="prefix" :item="item" :index="index"></slot>
|
||||
</template>
|
||||
<template v-slot="{ item, index }">
|
||||
<div class="item-title" @click.stop="emit('title',{item,index})">
|
||||
<div class="item-title" >
|
||||
<div class="name"> {{ `${searchKey ? '' : (index + 1) + '. '}${item.title}` }}</div>
|
||||
</div>
|
||||
<div class="item-sub-title" v-if="item.titleTranslate && showTranslate">
|
||||
|
||||
@@ -7,14 +7,12 @@ const props = withDefaults(defineProps<{
|
||||
activeIndex?: number,
|
||||
activeId?: string,
|
||||
isActive?: boolean
|
||||
showBorder?: boolean
|
||||
static?: boolean
|
||||
}>(), {
|
||||
list: [],
|
||||
activeIndex: -1,
|
||||
activeId: '',
|
||||
isActive: false,
|
||||
showBorder: false,
|
||||
static: true
|
||||
})
|
||||
|
||||
@@ -124,7 +122,6 @@ defineExpose({scrollToBottom, scrollToItem})
|
||||
<div class="common-list-item"
|
||||
:class="{
|
||||
active:itemIsActive(item,index),
|
||||
border:showBorder
|
||||
}"
|
||||
@click="emit('click',{item,index})"
|
||||
>
|
||||
@@ -154,7 +151,6 @@ defineExpose({scrollToBottom, scrollToItem})
|
||||
<div class="common-list-item"
|
||||
:class="{
|
||||
active:itemIsActive(item,index),
|
||||
border:showBorder
|
||||
}"
|
||||
@click="emit('click',{item,index})"
|
||||
>
|
||||
|
||||
@@ -362,7 +362,6 @@ defineExpose({getDictDetail, add, editDict})
|
||||
<BaseIcon
|
||||
style="position:absolute;right: 0;"
|
||||
title="大屏显示"
|
||||
@click="emitter.emit(EventKey.openArticleContentModal,article)"
|
||||
icon="iconoir:expand"/>
|
||||
</div>
|
||||
<div class="text" v-if="article.text">
|
||||
@@ -376,7 +375,6 @@ defineExpose({getDictDetail, add, editDict})
|
||||
<BaseIcon
|
||||
style="position:absolute;right: 0;"
|
||||
title="大屏显示"
|
||||
@click="emitter.emit(EventKey.openArticleContentModal,article)"
|
||||
icon="iconoir:expand"/>
|
||||
</div>
|
||||
<div class="text" v-if="getTranslateText(article).length">
|
||||
|
||||
@@ -15,6 +15,7 @@ import {useToast} from 'vue-toast-notification';
|
||||
import 'vue-toast-notification/dist/theme-sugar.css';
|
||||
import {getTranslateText} from "@/hooks/article.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import QuestionForm from "@/pages/pc/components/QuestionForm.vue";
|
||||
|
||||
interface IProps {
|
||||
article: Article,
|
||||
@@ -348,7 +349,6 @@ onMounted(() => {
|
||||
wrong = input = ''
|
||||
})
|
||||
emitter.on(EventKey.onTyping, onTyping)
|
||||
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
@@ -358,15 +358,106 @@ onUnmounted(() => {
|
||||
|
||||
defineExpose({showSentence, play, del, hideSentence, nextSentence})
|
||||
|
||||
|
||||
let list = $ref([
|
||||
{
|
||||
stem: "The writer thought_____?",
|
||||
options: [
|
||||
"he had lost his money",
|
||||
"someone had stolen his money",
|
||||
"the manager had the money",
|
||||
"the girl had stolen the money"
|
||||
],
|
||||
correctAnswer: ["b"],
|
||||
explanation: "根据课文第2~3行'I left the money in my room,'I said, and it's not there now',只有(b)someone had stolen his money 符合作者的推测。其他3个选择都不正确。"
|
||||
},
|
||||
{
|
||||
stem: "What had really happened?",
|
||||
options: [
|
||||
"The writer had lost the money.",
|
||||
"The girl had stolen the money.",
|
||||
"The manager had taken the money.",
|
||||
"Someone had stolen the money."
|
||||
],
|
||||
correctAnswer: ["a"],
|
||||
explanation: "根据课文的情节,只有(a)The writer had lost the money是正确的,符合课文的原义。(b)The girl had stolen themoney不符合课文的情况,因为是这个女孩捡到了钱,而不是她偷了钱;(c)The manager had taken the money 更与事实不符;(d)Someone had stolen the money与实际情况不符。"
|
||||
},
|
||||
{
|
||||
stem: "The money____ in his room.",
|
||||
options: ["was", "were", "are", "has"],
|
||||
correctAnswer: ["a"],
|
||||
explanation: "(b)were不符合语法,因为The money是单数不可数名词,故不能用were作谓语动词;(c)are 也不合乎语法,因为它也不能作money的谓语动词;(d)has不符合题义,若选(d)此句意思讲不通;只有(a)was合乎语法。"
|
||||
},
|
||||
{
|
||||
stem: "He could do nothing. He couldn't do____.",
|
||||
options: ["something", "nothing", "anything", "everything"],
|
||||
correctAnswer: ["c"],
|
||||
explanation: "只有(c)anything可以用在否定句中。(a)something不能用于否定句;(b)nothing若用在否定句中,双重否定会变成肯定意义的句子,不合题义;(d)everything 一般不用于这种否定句中。"
|
||||
},
|
||||
{
|
||||
stem: "A knock at the door____ him.",
|
||||
options: ["interrupted", "was interrupted", "interrupting", "was interrupting"],
|
||||
correctAnswer: ["a"],
|
||||
explanation: "只有(a)interrupted最合乎语法。(b)was interrupted是被动语态,这个句子不应该是被动语态。(c)interrupting是现在分词,不能作谓语。(d)was interrupting是过去进行时,interrupt(打断)是表示一个瞬间动作,即敲门声是一下子打断了他的话,而不是正在打断。故应该用一般过去时,不应该用过去进行时。"
|
||||
},
|
||||
{
|
||||
stem: "Where did she find the money? ____ the room.",
|
||||
options: ["Outside", "Out of", "Out", "Without"],
|
||||
correctAnswer: ["a"],
|
||||
explanation: "(a)Outside(prep.在……外)最符合逻辑,因为只有(a)能回答地点where。(b)Out of(prep.从……里面……)强调从里面向外,不合乎题义;(c)out不是介词,因此不能同 the room 构成表示地点的短语;(d)without(prep.没有)不表示地点,更不符合题义。"
|
||||
},
|
||||
{
|
||||
stem: "____ room was it? — This gentleman's.",
|
||||
options: ["To whom", "Who", "Whose", "Of whom"],
|
||||
correctAnswer: ["c"],
|
||||
explanation: "这是一个对定语(所有格)提问的疑问句。(a)To whom是对宾语提问;(b)Who是对主语提问;(d)Of whom 也是对宾语提问;只有(c)Whose(谁的)是对定语提问,所以应该选(c)。"
|
||||
},
|
||||
{
|
||||
stem: "The writer had lost his money. He felt upset. He must have been____.",
|
||||
options: ["sick", "ill", "worried", "tired"],
|
||||
correctAnswer: ["c"],
|
||||
explanation: "只有(c)worried(着急,忧虑)同前一句中的upset(不安)意思相近。(a)sick(有病的,恶心的)、(b)ill(有病的)与(d)tired(疲劳的,厌倦的)这3个选择都不合乎题义。"
|
||||
},
|
||||
{
|
||||
stem: "The manager was sympathetic. ____.",
|
||||
options: [
|
||||
"Everyone liked him",
|
||||
"He liked everyone",
|
||||
"He was sorry for the writer",
|
||||
"He liked the writer"
|
||||
],
|
||||
correctAnswer: ["c"],
|
||||
explanation: "只有(c)He was sorry for the writer(他为作者感到难过或惋惜)才能解释前面的句子The manager was sympathetic(表示同情的)。而其他3个选择(a)Everyone liked him(每个人都喜欢他)、(b)He liked everyone(他喜欢每个人)与(d)He liked the writer(他喜欢作者)都与前句意思不符。"
|
||||
},
|
||||
{
|
||||
stem: "He lost his money. His money was____.",
|
||||
options: ["losing", "missing", "going away", "disappearing"],
|
||||
correctAnswer: ["b"],
|
||||
explanation: "(a)losing(丢失)不正确。若选(a)主语应该是人,而不应该是money;(c)going away(走开,离开)词义不对;(d)disappearing(消失,失踪)词义不够恰当;只有(b)missing(丢掉的,失去的)词义最准确,而且可以作表语。"
|
||||
},
|
||||
{
|
||||
stem: "You can't post this letter without____.",
|
||||
options: ["an envelope", "a packet", "some string", "a pen"],
|
||||
correctAnswer: ["a"],
|
||||
explanation: "只有选(a)an envelope(信封)最合乎逻辑和事实。(b)apacket(一包)、(c)some string(一些细绳)和(d)a pen(一枝钢笔)这3个选择都不符合实际情况。"
|
||||
},
|
||||
{
|
||||
stem: "The girl returned the money. She was very____.",
|
||||
options: ["honourable", "honest", "honoured", "trusting"],
|
||||
correctAnswer: ["b"],
|
||||
explanation: "只有选(b)honest(诚实的)最合乎逻辑。(a)honourable(光荣的,体面的)、(c)honoured(感到荣幸的,受到尊敬的)与(d)trusting(信任的)这3个词都不如honest合乎逻辑。"
|
||||
}
|
||||
])
|
||||
let show = $ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="typing-article" ref="typeArticleRef">
|
||||
|
||||
<header class="mb-4">
|
||||
<div class="title word">{{ props.article.title }}</div>
|
||||
<div class="titleTranslate" v-if="settingStore.translate">{{ props.article.titleTranslate }}</div>
|
||||
</header>
|
||||
|
||||
<div class="article-content" ref="articleWrapperRef">
|
||||
<article :class="[
|
||||
settingStore.translate && 'tall',
|
||||
@@ -449,12 +540,15 @@ defineExpose({showSentence, play, del, hideSentence, nextSentence})
|
||||
</div>
|
||||
<div class="cursor" v-if="!isEnd" :style="{top:cursor.top+'px',left:cursor.left+'px'}"></div>
|
||||
</div>
|
||||
|
||||
<div class="options flex justify-center" v-if="isEnd">
|
||||
<BaseButton
|
||||
v-if="store.currentArticleDict.lastLearnIndex < store.currentArticleDict.articles.length - 1"
|
||||
@click="emitter.emit(EventKey.next)">下一章</BaseButton>
|
||||
@click="emitter.emit(EventKey.next)">下一章
|
||||
</BaseButton>
|
||||
</div>
|
||||
<div class="translate-bottom" v-if="settingStore.translate">
|
||||
|
||||
<div class="translate-bottom mb-10" v-if="settingStore.translate">
|
||||
<header class="mb-4">
|
||||
<div class="text-2xl center">{{ props.article.titleTranslate }}</div>
|
||||
</header>
|
||||
@@ -462,6 +556,18 @@ defineExpose({showSentence, play, del, hideSentence, nextSentence})
|
||||
<div class="text-xl mb-4 indent-8" v-for="t in getTranslateText(article)">{{ t }}</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="flex-center">
|
||||
<BaseButton @click="show =! show">显示题目</BaseButton>
|
||||
</div>
|
||||
<div class="toggle" v-if="show">
|
||||
<QuestionForm :questions="list"
|
||||
:duration="300"
|
||||
:immediateFeedback="false"
|
||||
:randomize="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -478,6 +584,7 @@ defineExpose({showSentence, play, del, hideSentence, nextSentence})
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
color: var(--color-article);
|
||||
font-size: 1.6rem;
|
||||
|
||||
header {
|
||||
word-wrap: break-word;
|
||||
@@ -504,7 +611,6 @@ defineExpose({showSentence, play, del, hideSentence, nextSentence})
|
||||
}
|
||||
|
||||
article {
|
||||
font-size: 1.6rem;
|
||||
line-height: 1.3;
|
||||
word-break: keep-all;
|
||||
word-wrap: break-word;
|
||||
|
||||
@@ -115,6 +115,7 @@ function getCurrentPractice() {
|
||||
|
||||
function saveArticle(val: Article) {
|
||||
console.log('saveArticle', val, JSON.stringify(val.lrcPosition))
|
||||
console.log('saveArticle', val.textTranslate)
|
||||
showEditArticle = false
|
||||
let rIndex = store.currentArticleDict.articles.findIndex(v => v.id === val.id)
|
||||
if (rIndex > -1) {
|
||||
@@ -347,9 +348,7 @@ const {playSentenceAudio} = usePlaySentenceAudio()
|
||||
<ArticleList
|
||||
:isActive="active"
|
||||
:static="false"
|
||||
:show-border="true"
|
||||
:show-translate="settingStore.translate"
|
||||
@title="e => emitter.emit(EventKey.openArticleContentModal,e.item)"
|
||||
@click="handleChangeChapterIndex"
|
||||
:active-id="articleData.article.id"
|
||||
:list="articleData.articles ">
|
||||
|
||||
@@ -132,7 +132,7 @@ export const DefaultBaseState = (): BaseState => ({
|
||||
type: DictType.article,
|
||||
resourceId: 'article_nce2',
|
||||
length: 96,
|
||||
lastLearnIndex:10
|
||||
lastLearnIndex:23
|
||||
},
|
||||
],
|
||||
wordDictList: [
|
||||
|
||||
@@ -7,7 +7,6 @@ export const EventKey = {
|
||||
changeDict: 'changeDict',
|
||||
openStatModal: 'openStatModal',
|
||||
openWordListModal: 'openWordListModal',
|
||||
openArticleContentModal: 'openArticleContentModal',
|
||||
openDictModal: 'openDictModal',
|
||||
openArticleListModal: 'openArticleListModal',
|
||||
closeOther: 'closeOther',
|
||||
|
||||
Reference in New Issue
Block a user