From a14d38b6f58ca98a1875de76b5eaebc49064d7ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E6=A2=A6?= Date: Thu, 30 Apr 2026 18:41:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=AD=94=E9=A2=98=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .components.d.ts | 8 + src/assets/AnswerQuestions/time.png | Bin 0 -> 1682 bytes src/components/lm/LmSelect.vue | 121 ++++ src/router/index.ts | 12 + src/views/AnswerQuestions/WrongRetrain.vue | 658 ++++++++++++++++++ .../components/ChoiceAnswerPanel.vue | 91 +++ .../components/QuestionNumberNav.vue | 75 ++ .../components/QuestionStemPanel.vue | 40 ++ .../components/UploadAnswerPanel.vue | 292 ++++++++ src/views/AnswerQuestions/components/types.ts | 22 + src/views/EnglishTools/index.vue | 33 +- src/views/Subjects/SubjectDetail.vue | 43 +- src/views/Subjects/VideoLearning.vue | 601 ++++++++++++++++ src/views/Subjects/WrongQuestions.vue | 343 +++++++-- 14 files changed, 2267 insertions(+), 72 deletions(-) create mode 100644 src/assets/AnswerQuestions/time.png create mode 100644 src/components/lm/LmSelect.vue create mode 100644 src/views/AnswerQuestions/WrongRetrain.vue create mode 100644 src/views/AnswerQuestions/components/ChoiceAnswerPanel.vue create mode 100644 src/views/AnswerQuestions/components/QuestionNumberNav.vue create mode 100644 src/views/AnswerQuestions/components/QuestionStemPanel.vue create mode 100644 src/views/AnswerQuestions/components/UploadAnswerPanel.vue create mode 100644 src/views/AnswerQuestions/components/types.ts create mode 100644 src/views/Subjects/VideoLearning.vue diff --git a/.components.d.ts b/.components.d.ts index dde0749..314f6c6 100644 --- a/.components.d.ts +++ b/.components.d.ts @@ -9,10 +9,17 @@ declare module 'vue' { export interface GlobalComponents { ActivityProgress: typeof import('./src/components/tasks/ActivityProgress.vue')['default'] AppHeader: typeof import('./src/components/layout/AppHeader.vue')['default'] + ChoiceAnswerPanel: typeof import('./src/components/answerQuestions/ChoiceAnswerPanel.vue')['default'] ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] + ElDialog: typeof import('element-plus/es')['ElDialog'] ElDrawer: typeof import('element-plus/es')['ElDrawer'] + ElOption: typeof import('element-plus/es')['ElOption'] + ElSelect: typeof import('element-plus/es')['ElSelect'] + LmSelect: typeof import('./src/components/lm/LmSelect.vue')['default'] LoadingState: typeof import('./src/components/loading/LoadingState.vue')['default'] PageFrame: typeof import('./src/components/layout/PageFrame.vue')['default'] + QuestionNumberNav: typeof import('./src/components/answerQuestions/QuestionNumberNav.vue')['default'] + QuestionStemPanel: typeof import('./src/components/answerQuestions/QuestionStemPanel.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] SubjectCard: typeof import('./src/components/cards/SubjectCard.vue')['default'] @@ -20,5 +27,6 @@ declare module 'vue' { TabBar: typeof import('./src/components/layout/TabBar.vue')['default'] TaskCard: typeof import('./src/components/cards/TaskCard.vue')['default'] TaskSidebar: typeof import('./src/components/tasks/TaskSidebar.vue')['default'] + UploadAnswerPanel: typeof import('./src/components/answerQuestions/UploadAnswerPanel.vue')['default'] } } diff --git a/src/assets/AnswerQuestions/time.png b/src/assets/AnswerQuestions/time.png new file mode 100644 index 0000000000000000000000000000000000000000..99fac83f6a09b37a81a50174e92f67559bc091da GIT binary patch literal 1682 zcmV;D25tF?P)Bz_=Yv!j}UBfbrXXD}31x$GvcDhU4dOoEQL%!+YWQK7M~V z+1S{48NPWMUtnnnU`(#X^E71D@g)B6OFZ8&02q&3@jTU+)4&SJH4CR#mX%-E4FHCx zEV>eo(Ez&StGJz2Z(12xaargaqOTbMv`+<*mV>ip&30E*VF{D(0J^krqw^wV z0xR7R&9V?co6@@+9~Qm2FtFlGmQTJjlLeqne($|$(Q;7AR9ND35iAPu zslZBRve*y}pe+|3GEt;#g(W_d#qE;;w4rpf-Bk%J-w>_aKhS&!&_ue?dB5x*l@6?A zLo^Sm0ML}rbKo!2xS{Y66g(ZGxKxVQ4 zl;IDVIIx;(2UfBnTJsb%-vO+WZglQHup%B(0iX=uH&}W0D%CWw4w8Qi&36EcC>y+} z*|7#11y-D+U_&&3RTNgrh*{lcyK7ouiC0-P-vKOA`My`nv=Lb8Ln^Kq0G#oMOlTCU zcC+0z4y@dU=oHC_%%D}fbfveZmtLo@(a`5_ZU z+N!X`6O;|n0K{8FwKh60(q>@!Ln<`+&P*1t-qMFmv{GG3Yk|c>DgY#EJ!GOtn-!L@ zAsT@AM(0+V?XK0pN;X9EkP2XZrOozA51A;^c7-LrA)4kpu%7(0U-_QxMY;$qHQ%YS zfc2zykUsb7TQ#0+&ucxfV)Nartgj5fI`Pl^w9RI_>!QLERayGSYQ%x1A+R3QX!qV# zquaHHJxn%SxZ^Z{zp7ZEeF@ zq_e<^R2J$ce@frG2H-OK#?F?4KUs%54Xn5{d|+8WOIHqP!ULDf*Opxw)Mxy@b*$?O z%i@)Shn9^x==L+W48Uce&$8}!rO)=mF0KmZZCFp{^ZBRY^e7yg;rKHgzZrlxk$P5I zEopPJ!s>nqt7q1O-U3TxXdMjxYUTS#VKp0ppV{0YxKA58Aj#dsTx3mJ7k9 z*^PUaEfH01{bgBr5nRCV6jZuUwiUluKy8PhYJF34S%U_a#A@riaNLP6DY*U)$5SJ? zfFb0AN>o)n?t2NCH+W#V5L{naR&8x1FaKj%e@kb=3+RsDDcJ9};1pEbT~4o8F`B?~ zF}Uv7S;ggaRq@|={y!WimW9}f0Vqqs^maJj568QfUG4kG&OZcMOFyk}_Qn!aZghd= z;!;u7*6n!SGyq*uNNGGl_pq7{(NHgAw1JfuD=*D)eGt$3zk@=JAOp%%5NXTYQF4AZ cwn}Q5*F88A!eK?7V*mgE07*qoM6N<$g8F0)SpWb4 literal 0 HcmV?d00001 diff --git a/src/components/lm/LmSelect.vue b/src/components/lm/LmSelect.vue new file mode 100644 index 0000000..802a505 --- /dev/null +++ b/src/components/lm/LmSelect.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/src/router/index.ts b/src/router/index.ts index e3cfa5a..c100875 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -21,12 +21,24 @@ const router = createRouter({ component: () => import('@/views/Subjects/TextbookDetail.vue'), meta: { hideLayout: true }, }, + { + path: '/subjects/:subject/video/:bookId', + name: 'video-learning', + component: () => import('@/views/Subjects/VideoLearning.vue'), + meta: { hideLayout: true }, + }, { path: '/subjects/:subject/wrong-questions', name: 'wrong-questions', component: () => import('@/views/Subjects/WrongQuestions.vue'), meta: { hideLayout: true }, }, + { + path: '/answer-questions/wrong-retrain', + name: 'wrong-retrain-answer', + component: () => import('@/views/AnswerQuestions/WrongRetrain.vue'), + meta: { hideLayout: true }, + }, { path: '/tasks', name: 'tasks', diff --git a/src/views/AnswerQuestions/WrongRetrain.vue b/src/views/AnswerQuestions/WrongRetrain.vue new file mode 100644 index 0000000..06bc892 --- /dev/null +++ b/src/views/AnswerQuestions/WrongRetrain.vue @@ -0,0 +1,658 @@ + + + + + diff --git a/src/views/AnswerQuestions/components/ChoiceAnswerPanel.vue b/src/views/AnswerQuestions/components/ChoiceAnswerPanel.vue new file mode 100644 index 0000000..62daddc --- /dev/null +++ b/src/views/AnswerQuestions/components/ChoiceAnswerPanel.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/src/views/AnswerQuestions/components/QuestionNumberNav.vue b/src/views/AnswerQuestions/components/QuestionNumberNav.vue new file mode 100644 index 0000000..54d2b79 --- /dev/null +++ b/src/views/AnswerQuestions/components/QuestionNumberNav.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/src/views/AnswerQuestions/components/QuestionStemPanel.vue b/src/views/AnswerQuestions/components/QuestionStemPanel.vue new file mode 100644 index 0000000..16ecd3d --- /dev/null +++ b/src/views/AnswerQuestions/components/QuestionStemPanel.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/src/views/AnswerQuestions/components/UploadAnswerPanel.vue b/src/views/AnswerQuestions/components/UploadAnswerPanel.vue new file mode 100644 index 0000000..d677e63 --- /dev/null +++ b/src/views/AnswerQuestions/components/UploadAnswerPanel.vue @@ -0,0 +1,292 @@ + + + + + diff --git a/src/views/AnswerQuestions/components/types.ts b/src/views/AnswerQuestions/components/types.ts new file mode 100644 index 0000000..1e547e0 --- /dev/null +++ b/src/views/AnswerQuestions/components/types.ts @@ -0,0 +1,22 @@ +export type AnswerQuestion = { + id: number; + subject: string; + questionType: string; + difficulty: string; + score: number; + duration: number; + typeLabel: string; + answerType: "choice" | "upload"; + stem: string; + options?: Array<{ value: string; label: string }>; + userAnswer?: string; + correctAnswer?: string; + isCorrect?: boolean; + answerText?: string; + explanation?: string; +}; + +export type UploadAnswerValue = { + name: string; + url: string; +}; diff --git a/src/views/EnglishTools/index.vue b/src/views/EnglishTools/index.vue index 429ebc1..1a8ac42 100644 --- a/src/views/EnglishTools/index.vue +++ b/src/views/EnglishTools/index.vue @@ -51,14 +51,11 @@ const tools = [ "单词闯关", "单词测评", "词典", - "单词书", - "单词闯关", - "单词测评", - "词典", - "单词书", - "单词闯关", - "单词测评", - "词典", + "生词本", + "乐读", + "乐训语感", + "句子箱", + "零基础", ]; const pageSize = 9; @@ -108,9 +105,7 @@ function onMouseUp(e: MouseEvent) { flex: 1; min-height: 0; border-radius: 16px; - background: #d8e1f0; - // padding: 16px 16px 14px; - gap: 14px; + gap: 16px; position: relative; } @@ -156,12 +151,12 @@ function onMouseUp(e: MouseEvent) { align-items: flex-start; border-radius: 12px; background: #fff; - padding: 28px; + padding: 52px 64px; box-shadow: 0 4px 12px rgba(51, 83, 145, 0.08); cursor: pointer; transition: box-shadow 0.2s; - width: 554px; - height: 238px; + min-width: 0; + min-height: 0; &:hover { box-shadow: 0 6px 18px rgba(51, 83, 145, 0.15); @@ -180,23 +175,23 @@ function onMouseUp(e: MouseEvent) { align-items: center; gap: 8px; position: absolute; - bottom: -14px; + bottom: -22px; left: 0; right: 0; } .dot { width: 12px; -height: 6px; + height: 6px; border-radius: 4px; - background: #D6DEEB; + background: #d6deeb; cursor: pointer; transition: all 0.2s; &.active { width: 32px; -height: 6px; - background: #ADC9FF; + height: 6px; + background: #adc9ff; } } diff --git a/src/views/Subjects/SubjectDetail.vue b/src/views/Subjects/SubjectDetail.vue index 71cfc8d..99e4543 100644 --- a/src/views/Subjects/SubjectDetail.vue +++ b/src/views/Subjects/SubjectDetail.vue @@ -62,7 +62,7 @@ v-for="book in activeBooks" :key="book.id" :book="book" - @click="goToBook(book)" + @click="goToActiveBook(book)" /> @@ -125,6 +125,15 @@ const englishBooks: Book[] = [ { id: 13, year: null, level: null, name: "Join in 外研剑桥", subtitle: "(三年级上册)" }, ]; +const englishVideoBooks: Book[] = [ + { id: 101, year: "2012", level: null, name: "北师大版", subtitle: "(三年级上册)" }, + { id: 102, year: null, level: null, name: "Join in 外研剑桥", subtitle: "(三年级上册)" }, + { id: 103, year: "2012", level: "入门", name: "北师大版", subtitle: "(三年级上册)" }, + { id: 104, year: null, level: null, name: "人教版 PEP", subtitle: "(三年级上册)" }, + { id: 105, year: null, level: null, name: "译林版", subtitle: "(三年级上册)" }, + { id: 106, year: "2024", level: null, name: "外研版", subtitle: "(三年级上册)" }, +]; + const subjectAllBooksMap: Record = { 英语: englishBooks, 语文: [], @@ -132,8 +141,16 @@ const subjectAllBooksMap: Record = { 科学: [], }; +const subjectVideoBooksMap: Record = { + 英语: englishVideoBooks, + 语文: [], + 数学: [], + 科学: [], +}; + /** 当前科目下全部可选书本 */ const allBooks = computed(() => subjectAllBooksMap[subjectName.value] ?? []); +const videoBooks = computed(() => subjectVideoBooksMap[subjectName.value] ?? []); /** 已添加的书本 id(默认全部添加,模拟初始状态) */ const addedBookIds = ref(englishBooks.map(b => b.id)); @@ -147,7 +164,7 @@ function handleConfirm(selectedIds: number[]) { } const activeBooks = computed(() => { - if (activeTab.value === "video") return []; + if (activeTab.value === "video") return videoBooks.value; return allBooks.value.filter((b: Book) => addedBookIds.value.includes(b.id)); }); @@ -167,6 +184,28 @@ function goToBook(book: Book) { }); } +function goToVideoBook(book: Book) { + router.push({ + name: 'video-learning', + params: { + subject: subjectName.value, + bookId: book.id, + }, + query: { + name: book.name, + subtitle: book.subtitle, + }, + }); +} + +function goToActiveBook(book: Book) { + if (activeTab.value === "video") { + goToVideoBook(book); + return; + } + goToBook(book); +} + function goToWrongQuestions() { router.push({ name: 'wrong-questions', diff --git a/src/views/Subjects/VideoLearning.vue b/src/views/Subjects/VideoLearning.vue new file mode 100644 index 0000000..57b54ab --- /dev/null +++ b/src/views/Subjects/VideoLearning.vue @@ -0,0 +1,601 @@ + + + + + diff --git a/src/views/Subjects/WrongQuestions.vue b/src/views/Subjects/WrongQuestions.vue index 658dbd6..00962bd 100644 --- a/src/views/Subjects/WrongQuestions.vue +++ b/src/views/Subjects/WrongQuestions.vue @@ -19,39 +19,24 @@
-
-
-
@@ -61,12 +46,8 @@
-
+

{{ q.content }}

@@ -80,10 +61,7 @@ 正确率: - + {{ q.accuracy }}% @@ -91,11 +69,7 @@
- {{ q.subject }} + {{ q.subject }} {{ q.type }} {{ q.level }} {{ q.time }} @@ -106,10 +80,7 @@ -
@@ -119,12 +90,60 @@
+ + +
+

错题重练

+ +
+
选择条件
+
+ +
+
+ +
+
+ 题目抽取数量 (1-50题) +
+
+ +
{{ retrainCount }}
+ +
+
+ +
+ + +
+
+
@@ -426,11 +530,11 @@ function extendPractice(q: Question) { .question-card { background: #f2fbff; border-radius: 20px; -// padding: 28px 28px 20px; + // padding: 28px 28px 20px; display: flex; flex-direction: column; border: 1.5px solid #e1f2fa; -// gap: 16px; + // gap: 16px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04); &.card-poor { @@ -440,17 +544,18 @@ function extendPractice(q: Question) { .question-actions { - display: flex; - flex-direction: column; - background: #FFF; - border-radius: 20px; - padding: 22px 24px; - gap: 16px; + display: flex; + flex-direction: column; + background: #FFF; + border-radius: 20px; + padding: 22px 24px; + gap: 16px; } + .question-text { font-size: 28px; color: #121212; -// line-height: 1.5; + // line-height: 1.5; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -524,14 +629,17 @@ function extendPractice(q: Question) { color: #2d6fe0; background: rgba(45, 111, 224, 0.1); } + &.subject-语文 { color: #27ae60; background: rgba(39, 174, 96, 0.1); } + &.subject-数学 { color: #2d6fe0; background: rgba(45, 111, 224, 0.1); } + &.subject-科学 { color: #9b59b6; background: rgba(155, 89, 182, 0.1); @@ -589,4 +697,137 @@ function extendPractice(q: Question) { border: 1.5px solid #2d6fe0; color: #2d6fe0; } + +:global(.retrain-dialog) { + --el-dialog-padding-primary: 0; + border-radius: 26px; + box-shadow: none; +} + +:global(.retrain-dialog .el-dialog__header) { + display: none; +} + +:global(.retrain-dialog .el-dialog__body) { + padding: 0; +} + +:global(.retrain-dialog-overlay) { + background-color: rgba(0, 0, 0, 0.58); +} + +.retrain-panel { + width: 100%; + // min-height: 640px; + padding: 48px 56px; + border-radius: 26px; + background: #fff; +} + +.dialog-title { + margin-bottom: 52px; + color: #121212; + font-size: 32px; + font-weight: 700; + line-height: 1; + text-align: center; +} + +.dialog-section { + margin-bottom: 48px; +} + +.section-title { + margin-bottom: 24px; + color: #121212; + font-size: 24px; + font-weight: 600; + line-height: 1; +} + +.section-tip { + color: #9ca3af; + font-weight: 400; +} + +.condition-row { + display: flex; + align-items: center; + gap: 18px; +} + +.question-count-section { + margin-bottom: 56px; +} + +.counter-row { + display: flex; + align-items: center; + gap: 22px; +} + +.counter-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 46px; + height: 46px; + border: 1px solid #edf0f5; + border-radius: 50%; + background: #fff; + color: #222; + cursor: pointer; + font-size: 26px; + line-height: 1; + + &:disabled { + color: #d9dee7; + cursor: not-allowed; + } +} + +.counter-value { + display: flex; + align-items: center; + justify-content: center; + width: 168px; + height: 58px; + border-radius: 4px; + background: #f5f6f8; + color: #333; + font-size: 26px; + font-weight: 500; +} + +.dialog-actions { + display: flex; + justify-content: center; + gap: 48px; +} + +.dialog-btn { + width: 190px; + height: 72px; + border-radius: 40px; + cursor: pointer; + font-size: 26px; + font-weight: 600; + transition: opacity 0.2s; + + &:hover { + opacity: 0.86; + } +} + +.cancel-btn { + border: 2px solid #b7d4f2; + background: #fff; + color: #6ca8e8; +} + +.confirm-btn { + border: none; + background: #2f80ed; + color: #fff; +}