baiseventeen пре 2 година
родитељ
комит
affdc5cb9d

+ 14 - 0
src/apis/editor.ts

@@ -0,0 +1,14 @@
+import axiosInstance from "@/apis/axios.config";
+
+const EDITOR_PREFIX = "/api/onlyoffice"
+
+const editorApis = {
+    triggerForceSave(fileKey: string) {
+        return axiosInstance.post(`${EDITOR_PREFIX}/forcesave`, {
+            "c": "forcesave",
+            "key": fileKey
+        })
+    }
+}
+
+export default editorApis

+ 14 - 0
src/assets/global.css

@@ -0,0 +1,14 @@
+* {
+    padding: 0;
+    margin: 0;
+    font-family: v-sans !important;
+}
+
+body, html, #app {
+    height: 100%;
+}
+
+:root {
+    --main-color: #1677ff;
+    --boder-color: #d5d5d5;
+}

+ 146 - 0
src/layout/Header/Header.vue

@@ -0,0 +1,146 @@
+<!--
+  Description: 主页面Header布局
+  Author: BaiQi
+  Created Date: 2024-6-30
+-->
+<template>
+
+  <el-row class="header">
+    <!--产品图标与名称-->
+    <el-col :span="2" class="header-icons">
+      <div>
+        Logo
+      </div>
+      <div>
+        /name
+      </div>
+    </el-col>
+    <!--选择菜单-->
+    <el-col :span="10" class="header-menu">
+      <el-menu mode="horizontal" :default-active="activePage" @select="handleSelect" class="menu">
+        <template v-if="role == 'student'">
+          <el-menu-item v-for="item in studentMenuItems" :index="item.key" class="header-menu-item">
+            <div>
+              {{ item.value }}
+            </div>
+          </el-menu-item>
+        </template>
+        <template v-else>
+          <el-menu-item v-for="item in teacherMenuItems" :index="item.key" class="header-menu-item">
+            <div>
+              {{ item.value }}
+            </div>
+          </el-menu-item>
+        </template>
+      </el-menu>
+    </el-col>
+    <!--用户头像和昵称-->
+    <el-col :span="3" :offset="7">
+      <div class="header-userName">
+        <!--如果是登录状态-->
+        <template v-if="store.token !== ''">
+          <!--用户有头像-->
+          <template v-if="store.user.userAvatar !== ''">
+          <el-avatar :size="32" :shape="'circle'" :src="store.user.userAvatar"/>
+          </template>
+          <!--用户没有头像-->
+          <template v-else>
+            <el-avatar :size="32" :shape="'circle'"> {{ store.user.name.charAt(0) }}</el-avatar>
+          </template>
+          <div class="nickName">
+            {{store.user.name}}
+          </div>
+        </template>
+        <!--如果是未登录状态-->
+        <template v-else>
+          <el-avatar :size="32" :shape="'circle'"> User</el-avatar>
+          <div class="nickName">user</div>
+        </template>
+      </div>
+<!--      <el-dropdown @command="handleCommand" style="position: absolute;top: 35%">-->
+<!--        <span style="color: blue">-->
+<!--            user-->
+<!--            <el-icon class="el-icon&#45;&#45;right">-->
+<!--              <arrow-down />-->
+<!--            </el-icon>-->
+<!--        </span>-->
+<!--        <template #dropdown>-->
+<!--          <el-dropdown-menu>-->
+<!--            <el-dropdown-item command="logout">退出登录</el-dropdown-item>-->
+<!--          </el-dropdown-menu>-->
+<!--        </template>-->
+<!--      </el-dropdown>-->
+    </el-col>
+  </el-row>
+
+</template>
+
+<script setup lang="ts">
+  import router from "../../router/index.js";
+  import {getCurrentInstance, onMounted, ref} from "vue";
+  import './index.scss'
+  import {useStore} from "@/store";
+  import emitter from "@/utils/emitter";
+  import {useRoute} from "vue-router";
+
+  const store = useStore()
+  const role:'teachers' | 'student' = store.user.role === 'STUDENT' ?   'student':'teachers';
+
+  const studentMenuItems = [
+    {
+      value: "课程广场",
+      key: "CourseSquare"
+    },
+    {
+      value: "我的课程",
+      key: "MyCourse"
+    },
+    {
+      value: "我的作业",
+      key: "MyWork"
+    },
+  ]
+  const teacherMenuItems = [
+    {
+      value: "课程广场",
+      key: "CourseSquare"
+    },
+    {
+      value: "我的课程",
+      key: "MyCourse"
+    },
+    {
+      value: "我的批改",
+      key: "MyWork"
+    },
+  ]
+
+  const handleSelect = (index)=>{
+    emitter.emit('change-page', index)
+  }
+
+  const handleCommand = (command)=>{
+    if(command==="logout"){
+      window.sessionStorage.clear()
+      router.push({path: "/login"})
+    }
+  }
+
+  const route = useRoute()
+  let activePage = ref('')
+  //处理刷新页面时,菜单项和内容不匹配的问题
+  onMounted(()=>{
+    let path = route.path.split('/')[2].charAt(0).toUpperCase()+route.path.split('/')[2].slice(1)
+    if(path === 'Assignment'){
+      activePage.value = 'MyWork'
+    }else {
+      activePage.value = path
+    }
+  })
+</script>
+
+<script lang="ts">
+  export default {
+    name: "Layout"
+  }
+</script>

+ 10 - 0
src/types/dialogue.ts

@@ -0,0 +1,10 @@
+export interface IDialogue {
+    role: string;
+    content: string;
+}
+
+export interface IRewriteResult {
+    role: string;
+    content: string;
+    timestamp: number;
+}

+ 25 - 0
src/types/dto.ts

@@ -0,0 +1,25 @@
+export interface AssignmentDTO {
+    assignmentName: string; // 作业名称不能缺失
+    description: string; // 作业描述不能缺失
+    descriptionFile?: string; // 作业要求,Word/Pdf文件 (可选)
+    attachments?: string[]; // 作业附件 (可选)
+    startTime: string; // 起始时间不能缺失 (ISO 8601 日期时间字符串)
+    endTime: string; // 结束时间不能缺失 (ISO 8601 日期时间字符串)
+}
+
+export interface EnrollDTO {
+    studentId:number;
+    courseId:number;
+    enrollCode:string;
+}
+
+export interface CourseDTO {
+    courseName: string; // 课程名不能缺失
+    teacherIds: number[]; // 老师Id不能缺失
+    courseImages?: string[]; // 可选
+    targetGrade?: string; // 可选
+    startTime: string; // 起始时间不能缺失 (ISO 8601 日期时间字符串)
+    endTime: string; // 截止时间不能缺失 (ISO 8601 日期时间字符串)
+    description: string; // 课程描述不能缺失
+    enrollCode: string; // 选课码不能缺失
+}

+ 200 - 0
src/views/CoursePage/CoursePage.vue

@@ -0,0 +1,200 @@
+<!--
+  Description: 我的课程页面
+  Author: BaiQi
+  Created Date: 2024-7-2
+-->
+<template>
+  <el-card style="margin: 10px auto 0;height: 625px; width: 80%" shadow="never">
+    <el-button :icon="Refresh" circle></el-button>
+    <template v-if="role == 'teachers'">
+      <el-button @click="visible = !visible" color="#597d3b">新增课程</el-button>
+    </template>
+    <el-select
+        v-model="state"
+        placeholder="状态"
+        size="default"
+        style="width: 150px; margin-left: 10px;"
+    >
+      <el-option
+          v-for="item in stateOptions"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value"
+      />
+    </el-select>
+
+    <el-table :data="dataForm.records" height="500" style="width: 100%">
+      <el-table-column type="index"/>
+      <el-table-column prop="courseName" label="课程名称"/>
+      <el-table-column prop="description" label="描述"/>
+      <el-table-column prop="endTime" label="截止时间" sortable/>
+      <el-table-column>
+        <template #default="scope">
+          <el-button text @click="handleShowDetail(scope.row.courseId)" style="margin-right: 0; margin-left: 0;padding-left: 5px; padding-right: 5px">
+            <el-icon style="margin-right: 3px"><ZoomIn/></el-icon>
+            详情
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </el-card>
+
+  <el-dialog v-model="visible">
+    <el-form :model="newCourse">
+      <el-form-item label="课程名称">
+        <el-input v-model="newCourse.courseName" placeholder="请输入名称"></el-input>
+      </el-form-item>
+      <el-form-item label="面向年级">
+        <el-input v-model="newCourse.targetGrade" placeholder="请输入年级"></el-input>
+      </el-form-item>
+      <el-form-item label="描述">
+        <el-input v-model="newCourse.description" placeholder="请输入描述"></el-input>
+      </el-form-item>
+      <el-form-item label="选课码">
+        <el-input v-model="newCourse.enrollCode" placeholder="请输入选课码"></el-input>
+      </el-form-item>
+      <el-form-item label="开始时间">
+        <el-date-picker v-model="newCourse.startTime" type="datetime" placeholder="截止时间" style="width: 180px"/>
+      </el-form-item>
+      <el-form-item label="截止时间">
+        <el-date-picker v-model="newCourse.endTime" type="datetime" placeholder="截止时间" style="width: 180px"/>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="visible = false">取消</el-button>
+        <el-button type="primary" @click="handleSubmit()">确定</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script lang="ts" setup>
+  import useApis from "@/apis";
+  import {useStore} from "@/store";
+  import {onMounted, reactive, ref} from "vue";
+  import type {CourseVO} from "@/types/vo";
+  import {Refresh, ZoomIn} from "@element-plus/icons-vue";
+  import router from "@/router";
+  import type {CourseDTO} from "@/types/dto";
+  import {ElNotification} from "element-plus";
+
+  const apis = useApis()
+  const store = useStore()
+  const role:'teachers' | 'student' = store.user.role === 'STUDENT' ?   'student':'teachers';
+
+  let visible = ref(false)
+
+  let dataForm = reactive<{
+    records: CourseVO[],
+    current: number,// 当前页码
+    size: number, // 分页大小
+    pages: number, // 总页数
+    total: number
+  }>({
+    records: null,
+    current: null,
+    size: null,
+    pages: null,
+    total: null,
+  })
+
+  let newCourse = reactive<CourseDTO>({
+    courseName: "",
+    teacherIds: [store.user.id],
+    courseImages: [],
+    targetGrade: "",
+    startTime: "",
+    endTime: "",
+    description: "",
+    enrollCode: ""
+  })
+
+  function formatDate(date) {
+    const d = new Date(date);
+    const year = d.getFullYear();
+    const month = String(d.getMonth() + 1).padStart(2, '0');
+    const day = String(d.getDate()).padStart(2, '0');
+    const hours = String(d.getHours()).padStart(2, '0');
+    const minutes = String(d.getMinutes()).padStart(2, '0');
+    const seconds = String(d.getSeconds()).padStart(2, '0');
+    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+  }
+
+  const handleSubmit = () => {
+    newCourse.endTime = formatDate(newCourse.endTime)
+    newCourse.startTime = formatDate(newCourse.startTime)
+    apis.createCourse(newCourse)
+        .then((res:any) => {
+          if(res.data.code===200) {
+            ElNotification({
+              title: '添加成功',
+              type: 'success',
+            })
+          }else {
+            console.log(res.data)
+          }
+        })
+        .catch((err) => {
+          console.log(err)
+        })
+    visible.value = false
+  }
+
+  const handleShowDetail = (value:number) => {
+    // router.push(`/home/courseDetail/${value}`)
+    router.replace(`/home/courseDetail/${value}`);
+    // location.reload()
+  }
+
+  const fetchCourseByUserId = () => {
+    if (role === 'student') {
+      apis.getCourseByStudentId(store.user.id)
+          .then((res: any) => {
+            let data = res.data.data
+            console.log(data)
+            Object.assign(dataForm, {...data})
+          })
+          .catch((err: any) => {
+            console.error(err);
+          })
+          .finally(() => {
+          });
+    } else if (role === "teachers") {
+      apis.getCourseByTeacherId(store.user.id)
+          .then((res: any) => {
+            let data = res.data.data
+            console.log(data)
+            Object.assign(dataForm, {...data})
+          })
+          .catch((err: any) => {
+            console.error(err);
+          })
+          .finally(() => {
+          });
+    }
+  }
+
+  onMounted(() => {
+    fetchCourseByUserId()
+  })
+
+  // TODO:状态选择
+  let state = ref(null)
+  const stateOptions = [
+    {
+      value: 0,
+      label: "已结课"
+    },
+    {
+      value: 1,
+      label: "未结课"
+    },
+  ]
+</script>
+
+<script lang="ts">
+export default {
+  name: "CoursePage"
+}
+</script>

+ 65 - 0
src/views/CourseSquare/component/AllCourse/AllCourse.scss

@@ -0,0 +1,65 @@
+.course-content {
+  position: relative;
+  width: 100%;
+  overflow: hidden;
+  margin: 40px auto 0 auto;
+
+  .course-content-container {
+    overflow: hidden;
+    margin: 0 auto;
+    height: 100%;
+    width: 1100px;
+    background-color: #ffffff;
+    border: solid 1px #d0d0d0;
+    border-radius: 7px;
+
+    .course-items-container {
+      display: flex;
+      justify-content: center;
+      gap: 10px;
+      //background-color: red;
+      margin: 20px 0;
+      //height: 100%;
+      //width: 1100px;
+
+    }
+
+    .el-card__body {
+      padding: 0;
+    }
+
+    .course-item {
+      flex: 1;
+      max-width: 200px;
+      box-sizing: border-box;
+      //padding: 10px;
+      height: 270px;
+      line-height: 30px;
+      //margin-bottom: 10px;
+
+      .course-item-img {
+        width: 100%;
+        height: 100%;
+        object-fit: cover;
+      }
+
+      .course-item-content {
+        font-size: 14px;
+        padding: 0 10px;
+      }
+
+      &.active {
+        cursor: pointer;
+      }
+    }
+
+    .course-item-hidden {
+      flex: 1;
+      max-width: 200px;
+      height: 270px;
+      box-sizing: border-box;
+      line-height: 30px;
+      opacity: 0;
+    }
+  }
+}

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

@@ -0,0 +1,96 @@
+<!--
+  Description: 写作界面。
+  Author: BaiQi
+  Created Date: 2024-6-22
+-->
+<template>
+
+  <div class="my-work">
+
+    <div class="my-work-slider">
+      <AIChatter :dialogueId="dataForm.dialogueId" :messages="dataForm.messages" />
+
+      <Dictionaries />
+    </div>
+    <div class="my-work-input">
+      <Edit :engagement="dataForm.engagement"/>
+    </div>
+    <Slider :dialogueId="dataForm.dialogueId"/>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import AIChatter from "@/components/AIChatter/AIChatter.vue";
+  import Dictionaries from "@/views/Home/component/Dictionaries/index.vue";
+  import useApis from "@/apis";
+  import {useStore} from "@/store";
+  import router from "@/router"
+  import {onMounted, reactive, ref, watchEffect} from "vue";
+  import type {engagementVO} from "@/types/vo";
+  import type {IDialogue} from "@/types/dialogue.ts";
+  import Edit from "@/views/Home/component/Edit/index.vue";
+  import Slider from '@/views/Home/component/Silder/index.vue'
+  import "./index.scss"
+  import {useRoute} from 'vue-router'
+
+  // 获得路由中的assignmentId
+  const route = useRoute()
+  const assignmentId = Number(route.params.assignmentId)
+
+  const apis = useApis()
+  const store = useStore()
+
+  const dataForm = reactive<{
+    messages: IDialogue[],
+    dialogueId: string;
+    engagement: engagementVO
+  }>({
+    messages: [],
+    dialogueId: '',
+    engagement: {} as engagementVO
+  })
+
+  // 获取数据
+  async function fetchData(){
+    await apis.getAIDialogues(assignmentId, store.user.id)
+        .then((_res:any) => {
+          const data = _res.data.data
+          console.log(data)
+          Object.assign(dataForm, {
+            dialogueId: data?.dialogueId,
+            messages: data?.messages.content,
+            engagement: dataForm.engagement
+          })
+        })
+
+    await apis.getEngagement(store.user.id, assignmentId)
+        .then((_res:any) => {
+          console.log(_res)
+          Object.assign(dataForm, {
+            dialogueId: dataForm.dialogueId,
+            messages: dataForm.messages,
+            engagement: _res.data.data
+          })
+        }).catch((_err:any) => {
+          console.log(_err)
+          router.push("/login")
+        })
+  }
+
+  onMounted( () => {
+    console.log("获取数据")
+    console.log(assignmentId,"assassignmentId的值")
+    fetchData()
+  })
+
+</script>
+
+<script lang="ts">
+export default {
+  name: "EditPage"
+}
+</script>
+
+<!--<style module>-->
+<!--//@import "index.scss";-->
+<!--</style>-->