Flex
8 天以前 b115cefe21a1c1aadc6fd4311720217e2c88dc33
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
<template>
  <div class="absolute top-0 left-0 right-0 bottom-0 flex">
    <!-- 条件输入区 -->
    <Left
      :is-writing="isWriting"
      class="h-full"
      @submit="submit"
      @uploadsuccess="uploadsuccess"
      @reset="reset"
      v-model:content="text"
    />
    <!-- 输入区 -->
    <Right
      :is-writing="isWriting"
      @stop-stream="stopStream"
      ref="rightRef"
      class="flex-grow"
      v-model:content="writeResult"
      @update:content="handleContentChange"
    />
    <!-- 识别区 -->
    <Identify
      :is-writing="isWriting"
      @stop-stream="stopStream"
      ref="identifyRef"
      class="flex-grow"
      v-model:content="identifyResult"
    />
  </div>
</template>
 
<script setup lang="ts">
import Left from './components/Left.vue'
import Right from './components/Right.vue'
import Identify from './components/Identify.vue'
import {docparseApi, generateQuestionsApi, GenQuestionVO} from "@/api/digitalcourse/genQuestion";
import {difficultyMap, questionTypeMap} from "@/views/digitalcourse/utils/constants";
 
// 使用消息提示
const message = useMessage()
 
// 定义响应式变量
const text = ref('') // 题目要求
const writeResult = ref('') // 试题生成结果
const identifyResult = ref('') // 识别区结果
const isWriting = ref(false) // 是否正在写作中
const abortController = ref<AbortController>() // 写作进行中 abort 控制器(控制 stream 写作)
 
/** 停止 stream 生成 */
const stopStream = () => {
  abortController.value?.abort()
  isWriting.value = false
}
 
/** 试题生成 */
const rightRef = ref<InstanceType<typeof Right>>()
const submit = async (data: GenQuestionVO) => {
  isWriting.value = true
  try {
    // 获取题型和难度
    const questionType = questionTypeMap[data.questionType] || ''
    const difficulty = difficultyMap[data.difficulty] || ''
    // 调用API生成试题
    const response = await generateQuestionsApi({
      text: data.text,
      question_type: questionType,
      difficulty: difficulty,
      num_questions: data.numQuestions
    })
    console.log('response', response)
    // 解析返回的数据字符串为 JSON 对象
    let parsedData;
    try {
      parsedData = JSON.parse(response);
    } catch (e) {
      message.alert('解析返回数据失败')
      stopStream()
      return
    }
 
    // 处理生成的试题
    if (parsedData.question) {
      writeResult.value += `\n${parsedData.question}`; // 追加生成的内容
      // 解析试题内容为 JSON 格式
      identifyResult.value = JSON.stringify(parseQuestionsToJSON(writeResult.value));
      stopStream()
    } else {
      message.alert('没有正确返回试题内容')
      stopStream()
      return
    }
    // 滚动到底部
    await nextTick()
    rightRef.value?.scrollToBottom()
  } catch (error) {
    console.error('写作异常', error)
    stopStream()
  }
}
 
/** 处理文件上传成功 */
const uploadsuccess = async (data) => {
  isWriting.value = true
  try {
    console.log('上传成功', data)
    message.success('上传成功!')
    // 调用API解析文档
    const response = await docparseApi({
      type: 'text',
      fileUrl: data.fileUrl
    })
    console.log('response', response)
    // 将response转为string
    text.value = JSON.stringify(response)
    stopStream()
  } catch (error) {
    console.error('写作异常', error)
    stopStream()
  }
}
 
/** 解析试题内容为 JSON 格式 */
const parseQuestionsToJSON = (questionsString: string) => {
  // 去掉空行
  const cleanedString = questionsString.replace(/\n\s*\n/g, '\n');
  // 将试题字符串按题目序号或题型描述拆分为独立的题目块
  const questionsArray = cleanedString.trim().split(/(?=^\d)/gm);
  return questionsArray.map((questionBlock) => {
    console.log('questionBlock', questionBlock)
    // 将每个题目块按行拆分
    const lines = questionBlock.split('\n');
    const questionLine = lines[0]; // 题目行
    const items = []; // 选项部分
    let answer = ''; // 答案部分
    let difficulty = ''; // 难度部分
    let knowledge = ''; // 知识点部分
    let explan = ''; // 解析部分
 
    // 循环解析每一行内容,区分题目、选项、答案、难度、知识点和解析
    lines.forEach((line) => {
      if (/^[A-E]\.\s*/.test(line)) {
        // 如果行以选项字母 (A-E) 开头,表示这是一个选项
        items.push(line.replace(/^[A-E]\.\s*/, '')); // 去掉选项前的字母和空格
      } else if (line.startsWith('答案:') || line.startsWith('答案:')) {
        // 如果行以"答案:"开头,表示这是答案部分
        answer = line.replace('答案', '').replace(':','').replace(':','').trim();
      } else if (line.startsWith('难度:')) {
        // 如果行以"难度:"开头,表示这是难度部分
        difficulty = line.replace('难度:', '').trim();
      } else if (line.startsWith('知识点:')) {
        // 如果行以"知识点:"开头,表示这是知识点部分
        knowledge = line.replace('知识点:', '').trim();
      } else if (line.startsWith('解析:')) {
        // 如果行以"解析:"开头,表示这是解析部分
        explan = line.replace('解析:', '').trim();
      }
    });
 
    // 返回结构化的题目对象
    return {
      question: questionLine.replace(/^\d+\.\s*/, '').replace(/[(\(].*?[)\)]/, '').replace(/".*?"/, ''), // 去掉题目编号和题型描述
      type: getQuestionType(questionLine), // 获取题目类型
      difficulty: difficulty, // 题目难度
      items: items, // 选项列表
      answers: answer, // 答案列表(将答案拆分为字符数组)
      explan: explan, // 解析内容
      knowledges: knowledge // 知识点内容
    };
  });
};
 
/** 获取试题类型 */
const getQuestionType = (question: string) => {
  if (question.includes('单选题')) return 'single_choice';
  if (question.includes('多选题')) return 'multiple_choice';
  if (question.includes('判断题')) return 'true_false';
  if (question.includes('问答题')) return 'short_answer';
  if (question.includes('填空题')) return 'fill_in_the_blank';
  return 'unknown';
};
 
/** 实时处理手动输入的内容变化 */
const handleContentChange = (content: string) => {
  identifyResult.value = JSON.stringify(parseQuestionsToJSON(content));
};
 
/** 点击重置的时候清空写作的结果 */
const reset = () => {
  writeResult.value = ''
}
</script>