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 0000000..99fac83 Binary files /dev/null and b/src/assets/AnswerQuestions/time.png differ 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; +}