xiaoyu 2 anni fa
parent
commit
b5cbbacc32

+ 45 - 75
package-lock.json

@@ -9,6 +9,7 @@
       "version": "0.0.0",
       "dependencies": {
         "@onlyoffice/document-editor-vue": "^1.4.0",
+        "@vue-office/docx": "^1.6.2",
         "axios": "^1.7.2",
         "docx-preview": "^0.3.2",
         "element-plus": "^2.7.5",
@@ -16,6 +17,7 @@
         "pinia": "^2.1.7",
         "pinia-plugin-persistedstate": "^3.2.1",
         "vue": "^3.4.29",
+        "vue-demi": "^0.14.10",
         "vue-router": "^4.3.3"
       },
       "devDependencies": {
@@ -1129,6 +1131,23 @@
         "vscode-uri": "^3.0.8"
       }
     },
+    "node_modules/@vue-office/docx": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmmirror.com/@vue-office/docx/-/docx-1.6.2.tgz",
+      "integrity": "sha512-OHAoUHeY8nHjhWvwDhlPx+/rmRkxmqLpvPgtfCEOZ4H1c1LCdJ6eDbdV3152ww8dcdZ7fgGQu3fmSSaI7JwdpQ==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "@vue/composition-api": "^1.7.1",
+        "vue": "^2.0.0 || >=3.0.0",
+        "vue-demi": "^0.14.6"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@vue/compiler-core": {
       "version": "3.4.29",
       "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.4.29.tgz",
@@ -1292,31 +1311,6 @@
         "url": "https://github.com/sponsors/antfu"
       }
     },
-    "node_modules/@vueuse/core/node_modules/vue-demi": {
-      "version": "0.14.8",
-      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.8.tgz",
-      "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
-      "hasInstallScript": true,
-      "bin": {
-        "vue-demi-fix": "bin/vue-demi-fix.js",
-        "vue-demi-switch": "bin/vue-demi-switch.js"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/antfu"
-      },
-      "peerDependencies": {
-        "@vue/composition-api": "^1.0.0-rc.1",
-        "vue": "^3.0.0-0 || ^2.6.0"
-      },
-      "peerDependenciesMeta": {
-        "@vue/composition-api": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/@vueuse/metadata": {
       "version": "9.13.0",
       "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz",
@@ -1336,31 +1330,6 @@
         "url": "https://github.com/sponsors/antfu"
       }
     },
-    "node_modules/@vueuse/shared/node_modules/vue-demi": {
-      "version": "0.14.8",
-      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.8.tgz",
-      "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
-      "hasInstallScript": true,
-      "bin": {
-        "vue-demi-fix": "bin/vue-demi-fix.js",
-        "vue-demi-switch": "bin/vue-demi-switch.js"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/antfu"
-      },
-      "peerDependencies": {
-        "@vue/composition-api": "^1.0.0-rc.1",
-        "vue": "^3.0.0-0 || ^2.6.0"
-      },
-      "peerDependenciesMeta": {
-        "@vue/composition-api": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/acorn": {
       "version": "8.11.3",
       "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.11.3.tgz",
@@ -2908,31 +2877,6 @@
         "pinia": "^2.0.0"
       }
     },
-    "node_modules/pinia/node_modules/vue-demi": {
-      "version": "0.14.8",
-      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.8.tgz",
-      "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
-      "hasInstallScript": true,
-      "bin": {
-        "vue-demi-fix": "bin/vue-demi-fix.js",
-        "vue-demi-switch": "bin/vue-demi-switch.js"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/antfu"
-      },
-      "peerDependencies": {
-        "@vue/composition-api": "^1.0.0-rc.1",
-        "vue": "^3.0.0-0 || ^2.6.0"
-      },
-      "peerDependenciesMeta": {
-        "@vue/composition-api": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/postcss": {
       "version": "8.4.38",
       "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.38.tgz",
@@ -3456,6 +3400,32 @@
         }
       }
     },
+    "node_modules/vue-demi": {
+      "version": "0.14.10",
+      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
+      "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/vue-eslint-parser": {
       "version": "9.4.3",
       "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz",

+ 2 - 0
package.json

@@ -13,6 +13,7 @@
   },
   "dependencies": {
     "@onlyoffice/document-editor-vue": "^1.4.0",
+    "@vue-office/docx": "^1.6.2",
     "axios": "^1.7.2",
     "docx-preview": "^0.3.2",
     "element-plus": "^2.7.5",
@@ -20,6 +21,7 @@
     "pinia": "^2.1.7",
     "pinia-plugin-persistedstate": "^3.2.1",
     "vue": "^3.4.29",
+    "vue-demi": "^0.14.10",
     "vue-router": "^4.3.3"
   },
   "devDependencies": {

+ 1 - 0
src/apis/user.ts

@@ -9,6 +9,7 @@ const userApis = {
             ...userForm
         })
     },
+
     findUserById: (id:number) => {
         return axiosInstance.get(`${USER_PREFIX}`, {
             params: {

+ 10 - 8
src/views/CorrectPage/CorrectPage.vue

@@ -8,22 +8,23 @@
     <div class="my-correct">
       <!-- 写作题目 及 作文内容 -->
       <div class="my-correct-header">
-        <StudentWritingRequirements/> 
-        <StudentEssay/> 
+        <StudentWritingRequirements /> 
+        <!-- TODO:怎么这么难写!!! -->
+        <StudentEssay :studentId = "studentId" :assignmentId="assignmentId"/> 
       </div>
       
       <div class="right-side">
         <!-- TODO:AI评分及写作过程记录:框架已完成,内容调取待完成 -->
         <div class="my-correct-item">
-            <h2>AI评分:</h2><br>
+            <h2>历史记录</h2><br>
             <ContentSelector/>
         </div>
         
         <!-- 教师评分、修改意见及评价 -->
         <!--新修改:评分小组件放入Remark组件中了  -->
         <div class="my-correct-item">
-            <!-- 已完成,效果未知 是否是engagement也未知 -->
-            <Remark :engagement="dataForm.engagement"/>              
+            
+            <Remark :studentId = "studentId" :assignmentId="assignmentId"/>              
         </div>    
         
       </div>
@@ -55,11 +56,12 @@ import Remark from "../Home/component/Remark/Remark.vue";
 // 获得路由中的assignmentId
 const route = useRoute()
 const assignmentId = Number(route.params.assignmentId)
+const studentId = Number(route.query.studentId)
 
 const apis = useApis()
 const store = useStore()
 
-//TODO:直接搬的EditPage,不知是否需要修改
+//直接搬的EditPage,不知是否需要修改
 const dataForm = reactive<{
   messages: IDialogue[],
   dialogueId: string;
@@ -72,7 +74,7 @@ const dataForm = reactive<{
 
 // 获取数据
 async function fetchData(){
-  await apis.getAIDialogues(assignmentId, store.user.id)
+  await apis.getAIDialogues(assignmentId,studentId )
       .then((_res:any) => {
         const data = _res.data.data
         console.log(data)
@@ -83,7 +85,7 @@ async function fetchData(){
         })
       })
 
-  await apis.getEngagement(store.user.id, assignmentId)
+  await apis.getEngagement(studentId, assignmentId)
       .then((_res:any) => {
         console.log(_res)
         Object.assign(dataForm, {

+ 1 - 1
src/views/CorrectPage/index.scss

@@ -29,7 +29,7 @@
         border-radius: 5px;  
         padding: 15px;  
         box-shadow: 0 2px 4px rgba(0,0,0,0.1);
-        height: 40%;
+        flex-grow: 1;
         //border: 1px solid black;
         background-color: #fff;
         

+ 1 - 0
src/views/EditPage/EditPage.vue

@@ -57,6 +57,7 @@
           const data = _res.data.data
           Object.assign(dataForm, {
             dialogueId: data?.dialogueId,
+            //TODO:AI对话拿到的字符串
             messages: data?.messages.content,
             engagement: dataForm.engagement
           })

+ 12 - 12
src/views/Home/component/ContentSelector/ContentSelector.vue

@@ -3,13 +3,14 @@
   Author: XiaoYu
   Created Date: 2024-7-27
 -->
+<!-- TODO:还是demo,三个内容还不能用 -->
 <template>  
   <div class="content-selector">  
     <!-- 按钮组 -->  
     <div class="button-group">
-      <el-button type="success" class="submit-button" @click="selectContent(0)">AI对话</el-button>
-      <el-button type="success" class="submit-button" @click="selectContent(1)">字典查询</el-button>
-      <el-button type="success" class="submit-button" @click="selectContent(2)">浏览器搜索</el-button>  
+      <el-button color="#597D3B" class="submit-button" @click="selectContent(0)">AI对话</el-button>
+      <el-button color="#597D3B" class="submit-button" @click="selectContent(1)">字典查询</el-button>
+      <el-button color="#597D3B" class="submit-button" @click="selectContent(2)">浏览器搜索</el-button>  
     </div>  
   
     <!-- 内容输入框,带有实线边框 -->  
@@ -54,7 +55,7 @@ export default {
 .content-selector {  
   display: flex;  
   align-items: flex-start; /* 确保按钮和内容输入框垂直对齐 */  
-  padding: 20px;  
+  padding: 10px;  
 }  
   
 .button-group {  
@@ -68,12 +69,13 @@ export default {
   padding: 10px 20px;  
   font-size: 16px;  
   cursor: pointer;  
-  border-radius: 5px;  
+  border-radius: 5px; 
+  
 }  
   
-.button-group button:hover {  
+/* .button-group button:hover {  
   background-color: #e0e0e0;  
-}  
+}   */
   
 .content-input-box {  
   flex-grow: 1; /* 占据剩余空间 */  
@@ -83,13 +85,11 @@ export default {
   position: relative; /* 为内部元素定位做准备(如果需要的话) */  
 }  
   
-.content-item {  
-  /* 这里不需要特别的样式,除非你想对每个内容项进行特定的样式设置 */  
-}  
+ 
   
-.input-area {  
+textarea {  
   width: 100%; /* 宽度占满内容输入框 */  
-  height: 100px; /* 示例高度,可以根据需要调整 */  
+  height: 150px; /* 示例高度,可以根据需要调整 */  
   border: none; /* 移除默认的textarea边框 */  
   resize: none; /* 禁止用户调整大小 */  
   background-color: #f9f9f9; /* 可选的背景色 */  

+ 51 - 57
src/views/Home/component/Remark/Remark.vue

@@ -1,86 +1,80 @@
-
-<!-- 此组件效果未知 -->
+<!--
+  Description: 教师评分和批改组件
+  Author: XiaoYu
+  Created Date: 2024-7-18
+-->
 
 <template>  
-  <div class="remark">  
-    <!-- 评分小组件 -->  
-    <rating-component v-model="correction.score" />  
-  
-    <!-- 评论输入框 -->  
-    <textarea v-model="correction.remark" placeholder="请输入评语"></textarea>  
-  
+  <div>  
+    <!-- 评分小组件 -->
+     <div class="rating-component">
+      <rating-component v-model="correction.score" />  
+     </div>  
+    
+    <div class="textarea">
+      <!-- 评论输入框 -->  
+      <textarea v-model="correction.remark" placeholder="请输入评语"></textarea>  
+    </div>
+    
     <!-- 提交按钮 -->
-    <div> </div>  
-    <el-button type="success" @click="handleCorrect">提交批改</el-button>
+    <el-button color="#597D3B" @click="handleCorrect">提交批改</el-button>
   </div>  
 </template>  
   
 <script lang="ts">  
 import "./index.scss"
 import RatingComponent from '@/views/CorrectPage/component/RatingComponent.vue'; // 假设评分小组件是本地的Vue文件  
-import {useRoute} from 'vue-router'  
+import { useRoute } from 'vue-router'  
 import useApis from "@/apis";
-import {useStore} from "@/store";
-import {ElMessage} from "element-plus";
-import router from '@/router'
-
-const apis = useApis()
-const store = useStore()
-const route = useRoute()
-const messageApi = ElMessage;
+import { useStore } from "@/store";
+import { ElMessage } from "element-plus";
+import router from '@/router';
 
- // 成功操作的消息提示
- const success = (msg: string) => {
-      messageApi.success(msg);
-    };
+export default { 
+  //defineprops只能在setup中使用 否则就直接写进这里 
+  props: {
+    studentId: {
+      type: Number,
+      required: true
+    },
+    assignmentId: {
+      type: Number,
+      required: true
+    }
+  },
   
-    // 失败操作的消息提示
-  const error = (msg: string) => {
-      console.log('失败操作', msg);
-      messageApi.error(msg);
-    };
-
-    
-
-
-
-export default {  
   data() {  
     return {  
       // 初始化correction对象,它将被用作correctDTO的模板  
       correction: {  
         score: 0, 
-        remark:''
-      },  
-      // 可能还需要其他数据,比如teacherId, studentId, assignmentId等  
-      // 这些数据可能从props、Vuex、localStorage或其他地方获取  
-      teacherId: store.user.id,  
-      studentId: Number(route.params.studentId),
-      assignmentId: Number(route.params.assignmentId),   
+        remark: ''
+      }
     };  
   }, 
   
   methods: {  
     async handleCorrect() {  
-      
-      await apis.engagementCorrect(this.studentId,this.assignmentId,this.correction)
-        .then((_res: any) => {
-              console.log(_res);
-              success(_res.data.data);
-              router.push("/home/myHomework")
-            })
-            .catch((error: any) => {
-              console.error('提交失败', error);
-              error('提交失败');
-            });
-      
-    },
+      const apis = useApis();
+      try {
+        const response = await apis.engagementCorrect(this.studentId, this.assignmentId, this.correction);
+        console.log(response);
+        ElMessage.success(response.data.data);
+        router.push(`/home/myCorrect/assignment/${this.assignmentId}`);
+      } catch (err) {
+        console.error('提交失败', err);
+        ElMessage.error('提交失败');
+      }
+    }
   },  
   
   // 如果你使用的是单文件组件,不要忘了在components选项中注册评分小组件  
   components: {  
-    RatingComponent,  
-  }  
+    RatingComponent
+  }
 }  
 </script>  
   
+<style scoped>
+/* 添加你的样式 */
+</style>

+ 18 - 20
src/views/Home/component/Remark/index.scss

@@ -2,26 +2,24 @@
 .remark {  
   display: flex;  
   flex-direction: column; // 垂直布局  
-  gap: 20px; // 元素之间的间隙  
-  padding: 20px; // 内边距  
+  gap: 10px; // 元素之间的间隙  
+  padding: 10px; // 内边距  
   border: 1px solid #ccc; // 边框  
   border-radius: 4px; // 边框圆角  
-  background-color: #fff; // 背景色  
-  
-   
-  .rating-component {  
-    margin-bottom: 10px; // 下边距  
-  }  
-  
-  // 文本区域样式  
-  textarea {  
-    width: 100%; // 宽度占满父容器  
-    padding: 10px; // 内边距  
-    resize: none; // 禁止用户调整大小  
-    border: 1px solid #ccc; // 边框  
-    border-radius: 4px; // 边框圆角  
-    // 其他样式...  
-  }  
-  
+  background-color: #fff; // 背景色   
 
-}  
+} 
+.rating-component {  
+  margin-bottom: 10px; // 下边距  
+}  
+
+// 文本区域样式  
+textarea { 
+  margin-bottom: 10px; /* 为textarea底部留出空间给提交按钮 */ 
+  height:250px;
+  width: 98%; // 宽度占满父容器  
+  padding: 10px; // 内边距   
+  border: 1px solid #ccc; // 边框  
+  border-radius: 4px; // 边框圆角  
+  // 其他样式...  
+}   

+ 88 - 79
src/views/Home/component/StudentEssay/StudentEssay.vue

@@ -6,102 +6,111 @@
 
 
 <template>
-
-  <div class="student-essay">
-    <div class="student-essay-info">
-      <div class="student-essay-text">
-
-        <div class="title">学生作文</div>
-        <div class="item">
-                <span style="font-weight: 600">学生ID: </span>
-                {{data.studentId}}
-        </div>
-
-        <!--这里放学生作业  -->
-        <!-- 假设默认的作业文档就是.docx格式 -->
-        <!--通过插件docx preview 实现 npm i docx-preview --save  -->
-        <div ref="fileDiv" class="text"></div>
-        
-        
+    <div>
+      <div style="margin-left: 5%;">
+          <span style="font-weight: 600 " >学生姓名: </span>
+          {{ data.studentName }}
+          <span style="font-weight: 600 " >&nbsp;&nbsp;学生ID: </span>
+          {{ studentId }}
+          
       </div>
-      
 
+        <!-- 最后使用的在线预览是Office Web Viewer(微软) -->
+    <div class="iframe-wrapper">  
+        <iframe
+            :src="officeUrl"
+            frameborder="0"
+            width="100%"
+            height="560px"
+            
+
+        >
+        </iframe>
+    </div>
+            
+       
     </div>
-    
-    
-  </div>
 </template>
+<!-- TODO:文件无法渲染 -->
+<script setup lang="ts">
+import { onMounted, defineProps ,computed, reactive} from 'vue';
 
+import useApis from '@/apis'; //  API 钩子函数
 
-<script lang="ts" setup>
-import useApis from "@/apis";
-import axios from "axios";
-import {useRoute} from "vue-router";
-import {onMounted, reactive, ref} from "vue";
-import type {engagementVO} from "@/types/vo";
-import './studentEssay.scss'
-import 'docx-preview';
-// import exampleText from './exampleText.txt'; 
  
-
 const apis = useApis()
-const route = useRoute()
-const assignmentId = +route.params.assignmentId //string转number
-const studentId = +route.params.studentId//string转number
-//引入
-var docx = require("docx-preview");
-const fileDiv = ref(null);  
-
-// const textContent = ref(exampleText);
-
-let data = reactive<engagementVO>({
-  id: null,
-  assignmentId: null,
-  studentId: null,
-  score: null,
-  remark: "",
-  fileUrl: "",
-  version: null,
-  status: "",
-  fileKey: ""
-})
 
-const fetchData = async () => {  
-  const res = await useApis().getEngagement(studentId, assignmentId);  
-  Object.assign(data, res.data.data);  
-  loadFile();  
-};  
-  
-const loadFile = async () => {  
-  if (data.fileUrl) {  
-    try {  
-      const response = await axios({  
-        method: 'get',  
-        responseType: 'blob',  
-        url: data.fileUrl,  
-      });  
-      docx.renderAsync(response.data, fileDiv.value);  
-    } catch (error) {  
-      console.error('Failed to load file:', error);  
-    }  
-  }  
-};  
+let data = reactive({
+    loading: false,
+    docxUrl:"",
+    studentName:""
+})
 
+// 调用 API 获取文档 URL  
+const fetchDocxUrl = async (studentId: number, assignmentId: number) => {
+    data.loading = true;
+    await apis.getEngagement(studentId, assignmentId)
+        .then(_res => {
+            if (_res.data.code === 200 && _res.data.data.fileUrl)
+                data.docxUrl = _res.data.data.fileUrl
+            console.log(data)
+        }).catch(_err => {
+            console.log(_err)
+        }).finally(() => {
+            data.loading = false
+        })   
+};
+
+
+
+async function fetchUserAndPrint(studentId) {  
+    apis.findUserById(props.studentId)
+        .then((res:any)=>{
+            data.studentName = res.data.data.records[0].name
+        })    
+}  
+// Office Web Viewer(微软)实现在线预览
+const officeUrl = computed(() => {
+      return `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(data.docxUrl)}`;
+    });
+
+// 组件挂载时调用,请求数据的方法
 onMounted(() => {
-  fetchData()
-})
+    fetchDocxUrl(props.studentId, props.assignmentId)
+    fetchUserAndPrint(props.studentId)
+});
 
-</script>
+const props = defineProps<{
+        studentId: number;
+        assignmentId: number;
+    }>();
 
 
+</script>
 
 <script lang="ts">
 
-export default {  
-  name: "StudentEssay",  
-  
-};  
-</script>
 
 
+</script>
 
+<style scoped>
+.loading {
+    /* 加载状态的样式 */
+    text-align: center;
+    padding: 20px;
+}
+
+.error {
+    /* 错误消息的样式 */
+    color: red;
+    text-align: center;
+    padding: 20px;
+}
+.iframe-wrapper {  
+  margin-top: 20px; /* 上边距 */  
+  margin-bottom: 20px; /* 下边距 */  
+} 
+</style>
+
+<!-- 注意:确保在父组件或全局范围内注册了 VueOfficeDocx 组件 -->

+ 0 - 1
src/views/Home/component/StudentEssay/exampleText.txt

@@ -1 +0,0 @@
-Exploring the heart of Rural China, we turn our gaze to the fertile grounds of Jiangsu, a province renowned for its timeless beauty akin to the fabled landscapes of Suzhou and Hangzhou. Here, the land nurtures generations with a rich agricultural heritage, where rice paddies glisten under the sun, vegetables thrive in lush gardens, and the fragrance of harvest fills the air. Jiangsu's soil, a testament to China's agricultural prowess, not only sustains its people but also showcases the ingenuity and resilience embedded in every inch of this cherished land.

+ 2 - 1
src/views/LoginPage/component/Login.vue

@@ -57,7 +57,7 @@
 </template>
 
 <script lang="ts" setup>
-  import { ref, reactive } from 'vue';
+  import { ref, reactive,defineProps } from 'vue';
   import { useStore } from '@/store';
   import useApis from '@/apis';
   import router from "@/router";
@@ -65,6 +65,7 @@
   import "./index.scss"
   import { ElMessage } from 'element-plus';
 
+
   interface LoginProps {
     // 是否是手机号登录
     isCode?: boolean