Ver Fonte

feat: 搭建项目广场框架

yhd12345 há 4 anos atrás
pai
commit
ab04b41200

+ 13 - 7
src/components/GlobalHeader.vue

@@ -13,12 +13,15 @@
         课程广场
       </el-menu-item>
       <el-menu-item index="4">
-        测试广场
+        项目广场
       </el-menu-item>
       <el-menu-item index="5">
+        测试广场
+      </el-menu-item>
+      <el-menu-item index="6">
         个人中心
       </el-menu-item>
-      <el-menu-item v-if="isTeacher" index="6">
+      <el-menu-item v-if="isTeacher" index="7">
         管理
       </el-menu-item>
     </el-menu>
@@ -76,11 +79,13 @@ export default {
         ["1", "Portal"],
         ["2", "Task"],
         ["3", "Course"],
-        ["4", "Test"],
-        ["5", "Home"],
-        ["6", "Manage"],
-        ["7", "Problem"],
-        ["8", "CourseDetail"],
+        ["4", "Project"],
+        ["5", "Test"],
+        ["6", "Home"],
+        ["7", "Manage"],
+        ["8", "Problem"],
+        ["9", "CourseDetail"],
+        
       ]
     };
   },
@@ -105,6 +110,7 @@ export default {
       this.menuIndex.forEach(i => {
         if (i[1] === this.$route.name) {
           out = i[0];
+          console.log(out);
         }
       });
       return out;

+ 299 - 0
src/components/ProjectCard.vue

@@ -0,0 +1,299 @@
+<template>
+  <el-card class="project" shadow="hover" @click="toProjectInfo">
+
+    <div :style="{borderBottom: type==='courseProject' ? '30px solid #f5b86e' : '30px solid #8dc9e2'}"
+         style="float: right; width: 0; height: 0; position: absolute;right: 0;bottom: 0;border-left: 30px solid transparent;">
+      <div v-if="type==='courseProject'" style="padding: 0 4px 0 4px; position: absolute; right: 0; bottom: -30px; color: white">
+        D
+      </div>
+      <div v-else style="padding: 0 3px 0 3px; position: absolute; right: 0; bottom: -30px; color: white">
+        P
+      </div>
+    </div>
+
+    <el-row>
+      <el-col :span="16">
+        <el-col class="title">
+          {{ title }}
+        </el-col>
+      </el-col>
+      <el-col :span="8" class="status">
+        <el-tag v-if="state==='NOT_STARTED'" size="small" type="info">未开始</el-tag>
+        <el-tag v-else-if="state==='RUNNING_IN' && !isPublicTest" size="small" type="success">已参加</el-tag>
+        <el-tag v-else-if="state==='RUNNING_NOT' && !isPublicTest" size="small">未参加</el-tag>
+        <el-tag v-else-if="isPublicTest" size="small">开放中</el-tag>
+        <el-tag v-else-if="state==='FINISHED'" size="small" type="danger">已结束</el-tag>
+        <el-tag v-else-if="state==='CLOSED'" size="small" type="info">已关闭</el-tag>
+      </el-col>
+    </el-row>
+    <el-row class="course" v-if="course!==''">
+      {{ course }}
+    </el-row>
+    <el-row class="teacher">
+      {{ teacherName }}
+    </el-row>
+
+    <el-row class="time">
+      {{ timePeriod }}
+    </el-row>
+
+    <el-row v-if="type==='courseProject'" class="comment">
+      {{ projectVO.comment }}
+    </el-row>
+  </el-card>
+</template>
+
+<script>
+import {ElMessage, ElMessageBox} from "element-plus";
+import store from "@/store";
+
+export default {
+  name: "ProjectCard",
+  props: {
+    projectVO: Object,
+    isPublicTest: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  data() {
+    return {
+      teacher: Object,
+    };
+  },
+  computed: {
+    isLogin() {
+      return store.state.user.token;
+    },
+    isCreator() {
+      if (this.projectVO.projectId) {
+        return Number(this.projectVO.course.teacherId) === Number(this.$store.state.user.id);
+      }else{
+        return this.projectVO.creator.username === this.$store.state.user.phone;
+      }
+      // todo
+    },
+    isTeacher() {
+      return this.$store.getters.isTeacher;
+    },
+    isStudent() {
+      return this.$store.getters.isStudent;
+    },
+    type() {
+      if (this.projectVO.projectId) {
+        return "courseProject";
+      } else {
+        return "commonProject";
+      }
+    },
+    title() {
+      return this.projectVO.name;
+    },
+    course() {
+      if (this.type === "courseProject") {
+        return "所属课程: " + this.projectVO.course;
+      } else {
+        return "";
+      }
+    },
+    teacherName() {
+      if (this.type === "courseProject") {
+        return "教师: " + this.teacher.name;
+        // todo
+      } else {
+        return "教师: " + this.teacher;
+      }
+    },
+    timePeriod() {
+      return "时间: " + this.projectVO.startTime.substring(0, 16) + "至" + this.projectVO.endTime.substring(0, 16);
+    },
+    state() {
+      const now = new Date();
+      if (this.type === "courseProject") {
+        if (new Date(this.projectVO.endTime) < now) {
+          return "FINISHED";
+        } else if (new Date(this.projectVO.startTime) > now) {
+          return "NOT_STARTED";
+        } else if (this.projectVO.isJoined) {
+          return "RUNNING_IN";
+        } else {
+          return "RUNNING_NOT";
+        }
+      } else {
+        if (this.projectVO.status === "CLOSED") {
+          return "CLOSED";
+        } else if (this.projectVO.startAt * 1000 > now) {
+          return "NOT_STARTED";
+        } else if (this.projectVO.endAt * 1000 < now) {
+          return "FINISHED";
+        } else if (this.projectVO.joined) {
+          return "RUNNING_IN";
+        } else {
+          return "RUNNING_NOT";
+        }
+      }
+    },
+  },
+  async created() {
+    // if (this.type === "courseProject") {
+    //   this.teacher = (await this.$apis.getUserAPI(this.projectVO.course["teacherId"])).data.data;
+    // } else {
+    //   this.teacher = (await this.$apis.getNameByUsernameAPI(this.projectVO.creator.username)).data.data;
+    // }
+    // todo
+  },
+  methods: {
+    timestampToTime(timestamp) {
+      const date = new Date(timestamp * 1000);
+      const Y = date.getFullYear() + "-";
+      const M = (date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1) + "-";
+      const D = (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + " ";
+      const h = (date.getHours() < 10 ? "0" + date.getHours() : date.getHours()) + ":";
+      const m = (date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes());
+      return Y + M + D + h + m;
+    },
+    async toProjectInfo() {
+      if (!this.isLogin) {
+        ElMessage({
+          showClose: true,
+          message: "请先登录",
+          type: "warning",
+          duration: 1000,
+        });
+        await this.$router.push({name: "Auth", params: {login_to: "/portal"}});
+        return;
+      }
+
+      if (this.isCreator) {
+        // todo
+      } else if (this.isTeacher) {
+        this.$message.error("无权访问其他教师创建的项目");
+      } else {
+        let messageBoxContent = "";
+        let messageBoxTitle = "";
+        let confirmButtonText = "";
+        let cancelButtonText = "让我想想";
+        let nextURL = "";
+        if (this.type === "courseProject") {
+          messageBoxTitle = "courseProject";
+          if (this.projectVO.isJoined) {
+            // nextURL = "https://eval.seec.seecoder.cn/exam/" + this.projectVO.id + "/before-exam/?id_token=" + this.$store.state.user.token;
+            // window.open(nextURL);
+            // todo 项目详情页跳转到哪
+            return;
+          } else {
+            const projectId = (await this.$apis.getCourseByEvalId(this.projectVO.course.id)).data.data;
+            // todo API
+            if (projectId) {
+              const isProjectSelected = (await this.$apis.checkCourseSelection(courseId, this.$store.state.user.id)).data.data;
+              // todo API
+              if (isProjectSelected) {
+                messageBoxContent = "是否立即查看项目详情";
+                confirmButtonText = "立即查看";
+                // nextURL = "https://eval.seec.seecoder.cn/student/home/?id_token=" + this.$store.state.user.token;
+              } else {
+                messageBoxContent = "尚未加入项目,是否立即加入?";
+                confirmButtonText = "立即加入";
+                nextURL = "/course/" + courseId;
+              }
+            } else {
+              messageBoxContent = "是否立即查看项目详情";
+              confirmButtonText = "立即查看";
+              // nextURL = "https://eval.seec.seecoder.cn/student/home/?id_token=" + this.$store.state.user.token;
+            }
+          }
+        } else {
+          messageBoxTitle = "CommonProject";
+          if (this.projectVO.joined) {
+            // nextURL = "https://coder.seec.seecoder.cn/exam/" + this.projectVO.id + "?id_token=" + this.$store.state.user.token;
+            // window.open(nextURL);
+            return;
+          } else {
+            const projectId = (await this.$apis.getCourseByCoderId(this.projectVO.courseId)).data.data;
+            if (projectId) {
+              const isProjectSelected = (await this.$apis.checkCourseSelection(courseId, this.$store.state.user.id)).data.data;
+              if (isProjectSelected) {
+                messageBoxContent = "是否立即查看项目详情";
+                confirmButtonText = "立即查看";
+                // nextURL = "https://coder.seec.seecoder.cn/exam?id_token=" + this.$store.state.user.token;
+              } else {
+                messageBoxContent = "尚未加入项目,是否立即加入?";
+                confirmButtonText = "立即加入";
+                nextURL = "/project/" + projectId;
+              }
+            } else {
+              messageBoxContent = "是否立即查看项目详情";
+              confirmButtonText = "立即查看";
+              // nextURL = "https://coder.seec.seecoder.cn/exam?id_token=" + this.$store.state.user.token;
+            }
+          }
+        }
+        ElMessageBox.confirm(messageBoxContent, messageBoxTitle, {
+          distinguishCancelAndClose: true,
+          confirmButtonText,
+          cancelButtonText,
+        }).then(() => {
+          if (nextURL.startsWith("http")) {
+            window.open(nextURL);
+          } else {
+            this.$router.push(nextURL);
+          }
+        });
+      }
+    },
+  }
+};
+</script>
+
+<style scoped>
+.project {
+  min-height: 180px;
+  border-radius: 8px;
+  height: calc(100% - 14px);
+  position: relative;
+  border: 1px solid #cccccc;
+}
+
+.project:hover {
+  cursor: pointer;
+  border: 1px solid #aaaaaa;
+}
+
+.title {
+  font-weight: bold;
+  font-size: medium;
+}
+
+.status {
+  text-align: right;
+}
+
+.course {
+  font-size: 10px;
+  margin-top: 4px;
+  color: #999999;
+}
+
+.teacher {
+  font-size: 10px;
+  margin-top: 10px;
+  color: #999999;
+}
+
+.time {
+  margin-top: 4px;
+  font-size: 10px;
+  margin-bottom: 16px;
+  color: #999999;
+}
+
+.comment {
+  background-color: #f6f6f6;
+  color: #777777;
+  margin-top: 10px;
+  border-radius: 8px;
+  padding: 10px;
+  font-size: 10px;
+  margin-bottom: 16px;
+}
+</style>

+ 6 - 1
src/router/index.js

@@ -24,6 +24,11 @@ const routes = [
     name: "Task",
     component: () => import("../views/TaskPage/TaskPage.vue"),
   },
+  {
+    path: "/project",
+    name: "Project",
+    component: () => import("../views/ProjectPage/ProjectPage.vue"),
+  },
   {
     path: "/home",
     name: "Home",
@@ -78,7 +83,7 @@ const router = createRouter({
   routes,
 });
 
-const unauthorizedRoutes = ["Portal", "Auth", "Course", "Test"];
+const unauthorizedRoutes = ["Portal", "Auth", "Course", "Test","Project"];
 
 router.beforeEach((to, from, next) => {
   if (unauthorizedRoutes.indexOf(to.name) !== -1) {

+ 188 - 0
src/views/ProjectPage/ProjectPage.vue

@@ -0,0 +1,188 @@
+<template>
+  <el-row id="project-page-body">
+    <el-col :span="3">
+      <el-card style="position: fixed;">
+        <el-menu default-active="1" @select="handleSelect">
+          <el-menu-item index="0">
+            <el-icon>
+              <Back/>
+            </el-icon>
+            <span style="padding-left: 20px">返回首页</span>
+          </el-menu-item>
+          <el-menu-item index="1">
+            <el-icon>
+              <Document/>
+            </el-icon>
+            <span style="padding-left: 20px">公开项目</span>
+          </el-menu-item>
+          <el-menu-item index="2">
+            <el-icon>
+              <DocumentChecked/>
+            </el-icon>
+            <span style="padding-left: 20px">我的项目</span>
+          </el-menu-item>
+        </el-menu>
+      </el-card>
+
+    </el-col>
+    <el-col :span="2"/>
+    <el-col :span="18">
+      <el-card>
+        <template #header>
+          <span style="font-weight: bold; font-size: larger">
+            {{ activeName }}
+          </span>
+        </template>
+        <div v-if="this.activeIndex === '1'">
+          <el-tabs v-model="activeProjectPaneName" style="width: 100%">
+            <el-tab-pane label="课程下项目" name="courseProject">
+              <div v-for="(item, key) in courseProjectList" :key="key">
+                <div v-if="item.length !== 0">
+                  <div>
+                    <el-divider style="margin: 10px 0 20px 0" content-position="left">{{ key }}</el-divider>
+                  </div>
+                  <div class="project-page-main">
+                    <ProjectCard v-for="project in item" :key="project.id" :projectVO="project" :isPublicproject="true"/>
+                  </div>
+                </div>
+              </div>
+            </el-tab-pane>
+            <el-tab-pane label="非课程项目" name="commonProject">
+              <div v-for="(item, key) in commonProjectList" :key="key">
+                <div v-if="item.length !== 0">
+                  <div>
+                    <el-divider style="margin: 10px 0 20px 0;" content-position="left">{{ key }}</el-divider>
+                  </div>
+                  <div class="project-page-main">
+                    <ProjectCard v-for="project in item" :key="project.id" :projectVO="project"/>
+                  </div>
+                </div>
+              </div>
+            </el-tab-pane>
+          </el-tabs>
+        </div>
+
+        <div v-if="this.activeIndex === '2' && isStudent">
+          <el-tabs v-model="activeProjectPaneName_my" style="width: 100%">
+            <el-tab-pane label="课程下项目" name="courseProject">
+              <div v-for="(item, key) in courseProjectList_my" :key="key">
+                <div>
+                  <el-divider style="margin: 10px 0 20px 0" content-position="left">{{ key }}</el-divider>
+                </div>
+                <div class="project-page-main">
+                  <ProjectCard v-for="project in item" :key="project.id" :projectVO="project"/>
+                </div>
+              </div>
+            </el-tab-pane>
+            <el-tab-pane label="非课程项目" name="commonProject">
+              <div v-for="(item, key) in commonProjectList_my" :key="key">
+                <div>
+                  <el-divider style="margin: 10px 0 20px 0" content-position="left">{{ key }}</el-divider>
+                </div>
+                <div class="project-page-main">
+                  <projectCard v-for="project in item" :key="project.id" :projectVO="project"/>
+                </div>
+              </div>
+            </el-tab-pane>
+          </el-tabs>
+        </div>
+
+        <div v-if="this.activeIndex === '2' && isTeacher">
+          教师请前往
+          <span style="cursor: pointer;color: #409eff"
+                @click="$router.push('/manage')">管理页面</span>
+          查看相关项目。
+        </div>
+
+        <div v-if="this.activeIndex === '2' && !isLogin">
+          请先
+          <span style="cursor: pointer;color: #409eff"
+                @click="$router.push('/auth')">登录</span>
+        </div>
+      </el-card>
+    </el-col>
+  </el-row>
+</template>
+
+<script>
+import {Back, Document, DocumentChecked} from "@element-plus/icons-vue";
+import ProjectCard from "@/components/ProjectCard";
+
+export default {
+  name: "projectPage",
+  components: {ProjectCard, Back, Document, DocumentChecked,},
+  data() {
+    return {
+      activeIndex: "1",
+      courseProjectList: [],
+      commonProjectList: [],
+      courseProjectList_my: [],
+      commonProjectList_my: [],
+      activeProjectPaneName: "course",
+      activeProjectPaneName_my: "course",
+    };
+  },
+  computed: {
+    activeName() {
+      if (this.activeIndex === "1") {
+        return "公开项目";
+      } else {
+        return "我的项目";
+      }
+    },
+    isTeacher() {
+      return this.$store.getters.isTeacher;
+    },
+    isStudent() {
+      return this.$store.getters.isStudent;
+    },
+    isLogin() {
+      return this.$store.getters.isLogin;
+    },
+  },
+  mounted() {
+    // this.$apis.getRecommendEvalExamsGroupByCourse().then(res => {
+    //   this.evalExamList = res.data.data;
+    // });
+    // this.$apis.getRecommendCoderExamsGroupByCourse().then(res => {
+    //   this.coderExamList = res.data.data;
+    // });
+    // if (this.isStudent) {
+    //   this.$apis.getStudentEvalExamsGroupByCourse()
+    //     .then(res => {
+    //       this.evalExamList_my = res.data.data;
+    //     });
+    //   this.$apis.getStudentCoderExamsGroupByCourse()
+    //     .then(res => {
+    //       this.coderExamList_my = res.data.data;
+    //     });
+    // }
+  },
+  methods: {
+    handleSelect(index) {
+      if (index === "0") {
+        this.$router.push("portal");
+      }
+      this.activeIndex = index;
+    },
+  },
+};
+</script>
+
+<style scoped>
+#project-page-body {
+  margin: 40px 60px 80px 0;
+  min-height: calc(100vh - 340px);
+  position: relative;
+}
+
+.project-page-main {
+  display: grid;
+  grid-template-columns: 1fr 1fr 1fr 1fr;
+  grid-gap: 20px;
+}
+
+:deep(.el-divider) {
+  margin: 0;
+}
+</style>