|
@@ -15,7 +15,7 @@
|
|
|
<span>{{
|
|
<span>{{
|
|
|
props.showData.choiceQuestionListData[props.showData.workIndex]
|
|
props.showData.choiceQuestionListData[props.showData.workIndex]
|
|
|
.teststitle
|
|
.teststitle
|
|
|
- }}</span>
|
|
|
|
|
|
|
+ }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="c_t45_msg" v-if="props.roleType == 1">
|
|
<div class="c_t45_msg" v-if="props.roleType == 1">
|
|
|
<div>{{ lang.ssAnswerCount }} {{ props.workArray.length }}<span
|
|
<div>{{ lang.ssAnswerCount }} {{ props.workArray.length }}<span
|
|
@@ -120,7 +120,7 @@
|
|
|
<span>{{
|
|
<span>{{
|
|
|
props.showData.choiceQuestionListData[props.showData.workIndex]
|
|
props.showData.choiceQuestionListData[props.showData.workIndex]
|
|
|
.teststitle
|
|
.teststitle
|
|
|
- }}</span>
|
|
|
|
|
|
|
+ }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="c_t45_msg" v-if="props.roleType == 1">
|
|
<div class="c_t45_msg" v-if="props.roleType == 1">
|
|
|
<div>{{ lang.ssAnswerCount }} {{ props.workArray.length }}<span
|
|
<div>{{ lang.ssAnswerCount }} {{ props.workArray.length }}<span
|
|
@@ -493,21 +493,15 @@
|
|
|
</div>
|
|
</div>
|
|
|
<div class="c_t79_wd_content">
|
|
<div class="c_t79_wd_content">
|
|
|
<!-- 图片 -->
|
|
<!-- 图片 -->
|
|
|
- <img
|
|
|
|
|
- v-if="getFileType(lookWorkData.content.fileList[lookWorkIndex].url) === 'image'"
|
|
|
|
|
|
|
+ <img v-if="getFileType(lookWorkData.content.fileList[lookWorkIndex].url) === 'image'"
|
|
|
:src="lookWorkData.content.fileList[lookWorkIndex].url"
|
|
:src="lookWorkData.content.fileList[lookWorkIndex].url"
|
|
|
@click.stop="lookImage(lookWorkData.content.fileList[lookWorkIndex])" />
|
|
@click.stop="lookImage(lookWorkData.content.fileList[lookWorkIndex])" />
|
|
|
<!-- 视频 -->
|
|
<!-- 视频 -->
|
|
|
- <video
|
|
|
|
|
- v-else-if="getFileType(lookWorkData.content.fileList[lookWorkIndex].url) === 'video'"
|
|
|
|
|
- :src="lookWorkData.content.fileList[lookWorkIndex].url"
|
|
|
|
|
- controls
|
|
|
|
|
|
|
+ <video v-else-if="getFileType(lookWorkData.content.fileList[lookWorkIndex].url) === 'video'"
|
|
|
|
|
+ :src="lookWorkData.content.fileList[lookWorkIndex].url" controls
|
|
|
@click.stop="lookImage(lookWorkData.content.fileList[lookWorkIndex])" />
|
|
@click.stop="lookImage(lookWorkData.content.fileList[lookWorkIndex])" />
|
|
|
<!-- 文档/PDF/Office -->
|
|
<!-- 文档/PDF/Office -->
|
|
|
- <div
|
|
|
|
|
- v-else
|
|
|
|
|
- class="file-preview-card"
|
|
|
|
|
- @click.stop="lookImage(lookWorkData.content.fileList[lookWorkIndex])">
|
|
|
|
|
|
|
+ <div v-else class="file-preview-card" @click.stop="lookImage(lookWorkData.content.fileList[lookWorkIndex])">
|
|
|
<div class="file-preview-icon">
|
|
<div class="file-preview-icon">
|
|
|
{{ getFileIcon(getFileType(lookWorkData.content.fileList[lookWorkIndex].url)) }}
|
|
{{ getFileIcon(getFileType(lookWorkData.content.fileList[lookWorkIndex].url)) }}
|
|
|
</div>
|
|
</div>
|
|
@@ -776,6 +770,37 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+ <div class="aiAnalysis" style="margin-top:1rem ;"
|
|
|
|
|
+ v-if="processedWorkArray.length > 0 && lookWorkData === null && props.showData.toolType == '831' && props.roleType == 1"
|
|
|
|
|
+ @click.stop="clickContent(false)">
|
|
|
|
|
+ <div class="ai_header">
|
|
|
|
|
+ <div class="ai_title">
|
|
|
|
|
+ <svg viewBox="0 0 1024 1024" width="200" height="200">
|
|
|
|
|
+ <path
|
|
|
|
|
+ d="M512 170.666667C323.477333 170.666667 170.666667 323.477333 170.666667 512s152.810667 341.333333 341.333333 341.333333 341.333333-152.810667 341.333333-341.333333S700.522667 170.666667 512 170.666667zM85.333333 512C85.333333 276.352 276.352 85.333333 512 85.333333s426.666667 191.018667 426.666667 426.666667-191.018667 426.666667-426.666667 426.666667S85.333333 747.648 85.333333 512z">
|
|
|
|
|
+ </path>
|
|
|
|
|
+ <path
|
|
|
|
|
+ d="M693.013333 330.986667a42.666667 42.666667 0 0 1 10.304 43.648l-75.413333 226.282666a42.666667 42.666667 0 0 1-26.986667 26.986667l-226.282666 75.413333a42.666667 42.666667 0 0 1-53.973334-53.973333l75.434667-226.261333a42.666667 42.666667 0 0 1 26.986667-26.986667l226.282666-75.413333a42.666667 42.666667 0 0 1 43.648 10.304z m-222.72 139.306666l-41.685333 125.098667 125.077333-41.706667 41.706667-125.077333-125.077333 41.706667z">
|
|
|
|
|
+ </path>
|
|
|
|
|
+ </svg>{{ lang.ssAnalysis }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="ai_refresh" :class="{ 'disabled': currentAnalysis && currentAnalysis.loading }"
|
|
|
|
|
+ @click.stop="aiAnalysisRefresh831()">
|
|
|
|
|
+ {{ lang.ssAIGenerate }}
|
|
|
|
|
+ <svg viewBox="0 0 1024 1024" width="200" height="200">
|
|
|
|
|
+ <path
|
|
|
|
|
+ d="M875 483c-33.4 0-60.5 27.1-60.5 60.5v0.1C814.4 710.3 678.8 846 512 846S209.5 710.3 209.5 543.5 345.2 241 512 241c36.8 0 71.7 7.6 104.4 19.7-32 3-57.4 29.1-57.4 61.9 0 34.8 28.2 63 63 63h201.9c34.8 0 63-28.2 63-63V120c0-34.8-28.2-63-63-63s-63 28.2-63 63v81.4C691 150.5 605.2 120 512 120 278.1 120 88.5 309.6 88.5 543.5S278.1 967 512 967s423.5-189.6 423.5-423.5c0-33.4-27.1-60.5-60.5-60.5z">
|
|
|
|
|
+ </path>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="ai_content" v-if="currentAnalysis && currentAnalysis.json" v-html="currentAnalysis.json.text">
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="ai_updateTime" v-if="currentAnalysis">{{ lang.ssUpdateTime }}:{{ currentAnalysis.update_at }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
<div class="c_t72_workDetail" v-if="lookWorkData" @click.stop="clickContent(false)">
|
|
<div class="c_t72_workDetail" v-if="lookWorkData" @click.stop="clickContent(false)">
|
|
|
<div class="c_t72_wd_top">
|
|
<div class="c_t72_wd_top">
|
|
|
<img src="../../../assets/img/arrow_left.png" @click.stop="lookWork('')" v-if="!isCastScreen" />
|
|
<img src="../../../assets/img/arrow_left.png" @click.stop="lookWork('')" v-if="!isCastScreen" />
|
|
@@ -787,6 +812,8 @@
|
|
|
{{ lang.ssExitCastScreen }}
|
|
{{ lang.ssExitCastScreen }}
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
<div class="c_t831_wd_content">
|
|
<div class="c_t831_wd_content">
|
|
|
<!-- KWL 三栏 -->
|
|
<!-- KWL 三栏 -->
|
|
|
<div class="kwl-grid" v-if="lookWorkData.content">
|
|
<div class="kwl-grid" v-if="lookWorkData.content">
|
|
@@ -828,7 +855,8 @@
|
|
|
</div>
|
|
</div>
|
|
|
<div class="kwl-col__body">
|
|
<div class="kwl-col__body">
|
|
|
<template v-if="lookWorkData.content.learned.length">
|
|
<template v-if="lookWorkData.content.learned.length">
|
|
|
- <div v-for="(item, idx) in lookWorkData.content.learned" :key="'l-' + idx" class="kwl-item">{{ item }}
|
|
|
|
|
|
|
+ <div v-for="(item, idx) in lookWorkData.content.learned" :key="'l-' + idx" class="kwl-item">{{ item
|
|
|
|
|
+ }}
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
<div v-else class="kwl-empty">{{ lang.ssNone }}</div>
|
|
<div v-else class="kwl-empty">{{ lang.ssNone }}</div>
|
|
@@ -849,7 +877,10 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<previewImageTool ref="previewImageToolRef" />
|
|
<previewImageTool ref="previewImageToolRef" />
|
|
@@ -876,12 +907,8 @@
|
|
|
<line x1="6" y1="6" x2="18" y2="18" />
|
|
<line x1="6" y1="6" x2="18" y2="18" />
|
|
|
</svg>
|
|
</svg>
|
|
|
</div>
|
|
</div>
|
|
|
- <iframe
|
|
|
|
|
- v-if="docPreviewType === 'pdf' || docPreviewType === 'office'"
|
|
|
|
|
- :src="getDocPreviewUrl(docPreviewType, docPreviewUrl)"
|
|
|
|
|
- class="file-preview-frame"
|
|
|
|
|
- frameborder="0"
|
|
|
|
|
- ></iframe>
|
|
|
|
|
|
|
+ <iframe v-if="docPreviewType === 'pdf' || docPreviewType === 'office'"
|
|
|
|
|
+ :src="getDocPreviewUrl(docPreviewType, docPreviewUrl)" class="file-preview-frame" frameborder="0"></iframe>
|
|
|
<div v-else class="file-preview-office">
|
|
<div v-else class="file-preview-office">
|
|
|
<div class="file-preview-office-icon">📎</div>
|
|
<div class="file-preview-office-icon">📎</div>
|
|
|
<div class="file-preview-office-name">{{ docPreviewUrl.split('/').pop() }}</div>
|
|
<div class="file-preview-office-name">{{ docPreviewUrl.split('/').pop() }}</div>
|
|
@@ -2619,6 +2646,122 @@ ${a.content}\n`
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// ai应用
|
|
|
|
|
+const aiAnalysisRefresh831 = async () => {
|
|
|
|
|
+
|
|
|
|
|
+ let chatMsg = ``
|
|
|
|
|
+ console.log('processedWorkArray.value', processedWorkArray.value)
|
|
|
|
|
+ processedWorkArray.value.forEach((i) => {
|
|
|
|
|
+ if (typeof i.content === 'object') {
|
|
|
|
|
+ const c = i.content
|
|
|
|
|
+ const studentName = i.name || ''
|
|
|
|
|
+ if (c.know && c.know.length) {
|
|
|
|
|
+ chatMsg += `\n${studentName} - K|已知:\n${c.know.join(';')}\n`
|
|
|
|
|
+ }
|
|
|
|
|
+ if (c.want && c.want.length) {
|
|
|
|
|
+ chatMsg += `\n${studentName} - W|想知:\n${c.want.join(';')}\n`
|
|
|
|
|
+ }
|
|
|
|
|
+ if (c.learned && c.learned.length) {
|
|
|
|
|
+ chatMsg += `\n${studentName} - L|已学:\n${c.learned.join(';')}\n`
|
|
|
|
|
+ }
|
|
|
|
|
+ if (c.reflection) {
|
|
|
|
|
+ chatMsg += `\n${studentName} - 学习反思:\n${c.reflection}\n`
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ const msg = `# CONTEXT #
|
|
|
|
|
+课程数据:
|
|
|
|
|
+- 课程名称:${props.courseDetail.title}
|
|
|
|
|
+- 课程学科:${props.courseDetail.name}
|
|
|
|
|
+- 需要提交人数:${props.showData.workArray.length + props.showData.unsubmittedStudents.length}
|
|
|
|
|
+- 已提交人数:${props.showData.workArray.length}
|
|
|
|
|
+你是一名教学分析助手。请根据全班学生提交的 KWL 表格与学习反思内容,从班级整体视角进行简洁分析,帮助教师在课堂中快速了解学生情况。
|
|
|
|
|
+
|
|
|
|
|
+请按照以下 4 个维度分析:
|
|
|
|
|
+1. K|已知(Know)
|
|
|
|
|
+2. W|想知(Want to know)
|
|
|
|
|
+3. L|已学(Learned)
|
|
|
|
|
+4. 学习反思
|
|
|
|
|
+
|
|
|
|
|
+分析要求:
|
|
|
|
|
+- 每个维度先统计实际有效作答人数及占全班比例。
|
|
|
|
|
+- 只分析实际有学生填写的维度;如果某个维度无人填写,则不要输出该维度。
|
|
|
|
|
+- 学生未填写某个维度,不代表“不知道”“没学会”或“没有反思”,禁止据此推断。
|
|
|
|
|
+- 将意思相近的回答合并归纳,优先呈现班级共性。
|
|
|
|
|
+- 如存在明显误解、集中困惑或值得教师关注的问题,可简短指出。
|
|
|
|
|
+- 不逐个学生罗列,不评价个人,不排名。
|
|
|
|
|
+- 不要为了填满表格而强行生成结论;没有明显发现的内容可以省略。
|
|
|
|
|
+- 输出必须简洁,每个单元格尽量控制在 1–2 句话。
|
|
|
|
|
+
|
|
|
|
|
+请仅使用以下表格格式输出:
|
|
|
|
|
+
|
|
|
|
|
+| 维度 | 填写情况 | 班级主要情况 | 教师可关注 |
|
|
|
|
|
+|---|---|---|---|
|
|
|
|
|
+| K|已知 | X人,X% | 概括学生主要已有认知,最多2点 | 如有明显误解或差异则指出,否则写“-” |
|
|
|
|
|
+| W|想知 | X人,X% | 概括学生最集中的问题或兴趣,最多2点 | 值得后续回应的问题,最多1点 |
|
|
|
|
|
+| L|已学 | X人,X% | 概括学生主要学习收获,最多2点 | 未充分理解或存在混淆的内容,最多1点 |
|
|
|
|
|
+| 学习反思 | X人,X% | 概括主要收获、困难或学习感受,最多2点 | 后续教学可调整或跟进的重点,最多1点 |
|
|
|
|
|
+
|
|
|
|
|
+注意:
|
|
|
|
|
+- 无人填写的维度整行删除。
|
|
|
|
|
+- 避免空泛表达,例如“学生学习情况较好”“总体表现不错”。
|
|
|
|
|
+- 尽量使用具体主题、概念或问题名称。
|
|
|
|
|
+
|
|
|
|
|
+# 学生提交数据 #
|
|
|
|
|
+${chatMsg}`
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ console.log('workUrl', props.workUrl)
|
|
|
|
|
+ console.log('ai应用提示词', msg)
|
|
|
|
|
+ if (!currentAnalysis.value) {
|
|
|
|
|
+ aiAnalysisData.value.push({
|
|
|
|
|
+ pid: props.workId + (props.cid ? ',' + props.cid : ''),
|
|
|
|
|
+ index: props.showData.workIndex,
|
|
|
|
|
+ loading: true,
|
|
|
|
|
+ json: { text: '', echartsData: '' },
|
|
|
|
|
+ noEnd: true,
|
|
|
|
|
+ update_at: '',
|
|
|
|
|
+ create_at: '',
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ aiAnalysisData.value.find((item: any) => {
|
|
|
|
|
+ return item.pid === props.workId + (props.cid ? ',' + props.cid : '') && item.index === props.showData.workIndex
|
|
|
|
|
+ }).loading = true
|
|
|
|
|
+ aiAnalysisData.value.find((item: any) => {
|
|
|
|
|
+ return item.pid === props.workId + (props.cid ? ',' + props.cid : '') && item.index === props.showData.workIndex
|
|
|
|
|
+ }).json = { text: '', echartsData: '' }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ chat_stream(msg, 'a7741704-ba56-40b7-a6b8-62a423ef9376', props.userId, lang.lang, (event) => {
|
|
|
|
|
+ if (event.type === 'message') {
|
|
|
|
|
+ aiAnalysisData.value.find((item: any) => {
|
|
|
|
|
+ return item.pid === props.workId + (props.cid ? ',' + props.cid : '') && item.index === props.showData.workIndex
|
|
|
|
|
+ }).json.text = md.render(event.data)
|
|
|
|
|
+
|
|
|
|
|
+ aiAnalysisData.value.find((item: any) => {
|
|
|
|
|
+ return item.pid === props.workId + (props.cid ? ',' + props.cid : '') && item.index === props.showData.workIndex
|
|
|
|
|
+ }).loading = false
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (event.type === 'messageEnd') {
|
|
|
|
|
+ aiAnalysisData.value.find((item: any) => {
|
|
|
|
|
+ return item.pid === props.workId + (props.cid ? ',' + props.cid : '') && item.index === props.showData.workIndex
|
|
|
|
|
+ }).json.text = md.render(event.data)
|
|
|
|
|
+ aiAnalysisData.value.find((item: any) => {
|
|
|
|
|
+ return item.pid === props.workId + (props.cid ? ',' + props.cid : '') && item.index === props.showData.workIndex
|
|
|
|
|
+ }).noEnd = false
|
|
|
|
|
+ aiAnalysisData.value.find((item: any) => {
|
|
|
|
|
+ return item.pid === props.workId + (props.cid ? ',' + props.cid : '') && item.index === props.showData.workIndex
|
|
|
|
|
+ }).update_at = new Date().toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }).replace(/\//g, '-')
|
|
|
|
|
+ saveAnalysis()
|
|
|
|
|
+ }
|
|
|
|
|
+ }, '', [], 'open-qwen-plus-latest').catch(err => {
|
|
|
|
|
+ console.log('err', err)
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
// 当前分析
|
|
// 当前分析
|
|
|
const currentAnalysis = computed(() => {
|
|
const currentAnalysis = computed(() => {
|