soleil %!s(int64=7) %!d(string=hai) anos
pai
achega
e085cdc043

+ 124 - 0
mock/modules/assignment/index.js

@@ -0,0 +1,124 @@
+const express = require("express");
+const router = express.Router();
+const CodeSerializer = require("../../serializers/assignment/CodeSerializer");
+const ProjectSerializer = require("../../serializers/assignment/ProjectSerializer");
+const CodeResultSerializer = require("../../serializers/assignment/CodeResultSerializer");
+const DocumentSerializer = require("../../serializers/assignment/DocumentSerializer");
+const DocumentResultSerializer = require("../../serializers/assignment/DocumentResultSerializer");
+
+//新建代码作业
+router.route("/course/:courseId/code").post((req, res) => {
+  res.send({
+    data: CodeSerializer()
+  });
+});
+
+router
+  .route("/code/:codeId")
+  //更新代码作业信息
+  .put((req, res) => {
+    res.send({
+      data: CodeSerializer()
+    });
+  })
+  //删除代码作业
+  .delete((req, res) => {
+    res.send({
+      data: {}
+    });
+  });
+
+//老师查看代码作业列表
+router.route("/teacher/course/:courseId/code").get((req, res) => {
+  res.send({
+    data: [CodeSerializer(), CodeSerializer()]
+  });
+});
+
+//学生查看代码作业列表
+router.route("/student/course/:courseId/code").get((req, res) => {
+  res.send({
+    data: [CodeSerializer(), CodeSerializer()]
+  });
+});
+
+//学生查看单个代码作业
+router.route("/student/code/:codeId").get((req, res) => {
+  res.send({
+    data: CodeSerializer()
+  });
+});
+
+//学生查看单个代码作业下的项目信息列表
+router.route("/student/code/:codeId/project").get((req, res) => {
+  res.send({
+    data: [ProjectSerializer(), ProjectSerializer()]
+  });
+});
+
+//老师查看单个代码作业的成绩列表(所有组的)
+router.route("/teacher/code/:codeId/result").get((req, res) => {
+  res.send({
+    data: [CodeResultSerializer, CodeResultSerializer]
+  });
+});
+
+//学生查看单个代码作业的成绩
+router.route("/student/code/:codeId/result").get((req, res) => {
+  res.send({
+    data: CodeResultSerializer
+  });
+});
+
+// Document
+//新建文档作业
+router.route("/course/:courseId/document").post((req, res) => {
+  res.send({
+    data: DocumentSerializer()
+  });
+});
+
+router
+  .route("/document/:documentId")
+  //更新文档作业信息
+  .put((req, res) => {
+    res.send({
+      data: DocumentSerializer()
+    });
+  })
+  //删除文档作业
+  .delete((req, res) => {
+    res.send({
+      data: {}
+    });
+  });
+
+//老师查看文档作业列表
+router.route("/teacher/course/:courseId/document").get((req, res) => {
+  res.send({
+    data: [DocumentSerializer(), DocumentSerializer()]
+  });
+});
+
+//学生查看文档作业列表
+router.route("/student/course/:courseId/document").get((req, res) => {
+  res.send({
+    data: [DocumentSerializer(), DocumentSerializer()]
+  });
+});
+
+//老师查看文档作业的成绩列表(所有小组)
+router.route("/teacher/document/:documentId/result").get((req, res) => {
+  res.send({
+    data: [DocumentResultSerializer(), DocumentResultSerializer()]
+  });
+});
+
+//学生查看文档作业的成绩
+router.route("/student/document/:documentId/result").get((req, res) => {
+  res.send({
+    data: DocumentResultSerializer()
+  });
+});
+
+module.exports = router;

+ 1 - 1
mock/modules/course/index.js

@@ -32,7 +32,7 @@ router
 
 router.route("/course/:courseId").get((req, res) => {
   res.send({
-    data: courseSerializer()
+    data: courseSerializer(req.params.courseId)
   });
 });
 

+ 1 - 0
mock/routes.js

@@ -6,6 +6,7 @@ router.use("/user", require("./modules/user"));
 router.use("/course", require("./modules/course"));
 router.use("/group", require("./modules/group"));
 router.use("/question", require("./modules/question"));
+router.use("/assignment", require("./modules/assignment"));
 router.use("/review", require("./modules/review"));
 
 module.exports = router;

+ 20 - 0
mock/serializers/assignment/NestedProblemSerializer.js

@@ -0,0 +1,20 @@
+const makeId = require("../../util/makeId");
+/**
+ * @description 代码作业题目简略信息
+ * @typedef {Object} NestedProblemSerializerType
+ * @property {number} id 代码题目id
+ * @property {string} name 题目名称
+ * @property {string} decription 题目描述
+ */
+
+/**
+ * @description 生成代码作业中题目的简略信息
+ * @returns {NestedProblemSerializerType}
+ */
+module.exports = () => {
+  return {
+    id: makeId(),
+    name: "完型填空",
+    description: "请完成那些完型填空题目啊"
+  };
+};

+ 24 - 0
mock/serializers/assignment/ScoreSerializer.js

@@ -0,0 +1,24 @@
+const makeId = require("../../util/makeId");
+/**
+ * @description 成绩条目
+ * @typedef {Object} ScoreSerializerType
+ * @property {number} id 条目编号
+ * @property {string} name 条目名称
+ * @property {string} score 得分
+ */
+
+/**
+ * @description 生成条目成绩
+ * @returns {ScoreSerializerType}
+ */
+
+module.exports = () => {
+  // function f() {
+  return {
+    id: makeId(),
+    name: "测试",
+    score: "90"
+  };
+};
+
+// console.log(f());

+ 2 - 2
mock/serializers/course/CourseSerializer.js

@@ -14,9 +14,9 @@ const userSerializer = require("../user/UserSerializer");
  * @description 生成课程信息
  * @returns {CourseSerializerType}
  */
-module.exports = () => {
+module.exports = (id = makeId()) => {
   return {
-    id: makeId(),
+    id,
     name: "软件工程第二阶段 2018",
     code: "xxxxx",
     teacher: userSerializer("TEACHER")

+ 1 - 0
src/api/_prefix.js

@@ -7,4 +7,5 @@ export const TAG_MODULE = `${API_VERSION}/tag`;
 export const COURSE_MODULE = `${API_VERSION}/course`;
 export const GROUP_MODULE = `${API_VERSION}/group`;
 export const QUESTION_MODULE = `${API_VERSION}/question`;
+export const ASSIGNMENT_MODULE = `${API_VERSION}/assignment`;
 export const REVIEW_MODULE = `${API_VERSION}/review`;

+ 200 - 0
src/api/assignment.js

@@ -0,0 +1,200 @@
+import request from "@/util/request";
+import { ASSIGNMENT_MODULE } from "./_prefix";
+
+/* 代码作业*/
+/**
+ * 老师新建代码作业
+ * @param type
+ * @param name
+ * @param description
+ * @param startAt
+ * @param endAt
+ * @param problem
+ * @returns {Promise<{data:CodeSerializerType}>}
+ */
+export const postCodeAssignment = ({
+  type,
+  name,
+  description,
+  startAt,
+  endAt,
+  problem
+}) => {
+  return request(`${ASSIGNMENT_MODULE}/course/:courseId/code`, {
+    method: "POST",
+    body: {
+      type: type || null,
+      name: name || null,
+      description: description || null,
+      startAt: startAt || null,
+      endAt: endAt || null,
+      problem: problem || null
+    }
+  });
+};
+/**
+ * 老师更新代码作业信息
+ * @param name
+ * @param description
+ * @param startAt
+ * @param endAt
+ * @returns {Promise<{data:CodeSerializerType}>}
+ */
+export const putCodeAssignment = ({ name, description, startAt, endAt }) => {
+  return request(`${ASSIGNMENT_MODULE}/code/:codeId`, {
+    method: "PUT",
+    body: {
+      name: name || null,
+      description: description || null,
+      startAt: startAt || null,
+      endAt: endAt || null
+    }
+  });
+};
+
+/**
+ * 老师删除代码作业
+ * @returns {Promise<{}>}
+ */
+export const delCodeAssignment = () => {
+  return request(`${ASSIGNMENT_MODULE}/code/:codeId`);
+};
+
+/**
+ * 老师查看代码作业列表
+ * @returns {Promise<{data:CodeSerializerType[]}>}
+ */
+export const getCodeAssignmentListByTeacher = () => {
+  return request(`${ASSIGNMENT_MODULE}/teacher/course/:courseId/code`);
+};
+
+/**
+ * 学生查看代码作业列表
+ * @returns {Promise<{data:CodeSerializerType[]}>}
+ */
+export const getCodeAssignmentListByStu = () => {
+  return request(`${ASSIGNMENT_MODULE}/student/course/:courseId/code`);
+};
+
+/**
+ * 学生查看单个代码作业列表
+ * @returns {Promise<{data:CodeSerializerType}>}
+ */
+export const getCodeAssignmentByStu = () => {
+  return request(`${ASSIGNMENT_MODULE}/student/code/:codeId`);
+};
+
+/**
+ * 学生查看单个代码作业下的项目信息列表
+ * @returns {Promise<{data:ProjectSerializerType}>}
+ */
+export const getProjectListByCode = () => {
+  return request(`${ASSIGNMENT_MODULE}/student/code/:codeId/project`);
+};
+
+/**
+ * 学生查看单个代码作业下的成绩
+ * @returns {Promise<{data:CodeResultSerializerType}>}
+ */
+export const getCodeResultByCode = () => {
+  return request(`${ASSIGNMENT_MODULE}/student/code/:codeId/result`);
+};
+
+/**
+ * 老师查看单个代码作业的成绩列表(所有组的)
+ * @returns {Promise<{data:CodeResultSerializerType}>}
+ */
+export const getCodeResultListByCode = () => {
+  return request(`${ASSIGNMENT_MODULE}/teacher/code/:codeId/result`);
+};
+
+/* 文档作业*/
+/**
+ * 老师新建文档作业
+ * @param type
+ * @param name
+ * @param description
+ * @param startAt
+ * @param endAt
+ * @param problem
+ * @returns {Promise<{data:DocumentSerializerType}>}
+ */
+export const postDocAssignment = ({
+  type,
+  name,
+  description,
+  startAt,
+  endAt,
+  problem
+}) => {
+  return request(`${ASSIGNMENT_MODULE}/course/:courseId/document`, {
+    method: "POST",
+    body: {
+      type: type || null,
+      name: name || null,
+      description: description || null,
+      startAt: startAt || null,
+      endAt: endAt || null,
+      problem: problem || null
+    }
+  });
+};
+/**
+ * 老师更新文档作业信息
+ * @param name
+ * @param description
+ * @param startAt
+ * @param endAt
+ * @returns {Promise<{data:DocumentSerializerType}>}
+ */
+export const putDocAssignment = ({ name, description, startAt, endAt }) => {
+  return request(`${ASSIGNMENT_MODULE}/document/:documentId`, {
+    method: "PUT",
+    body: {
+      name: name || null,
+      description: description || null,
+      startAt: startAt || null,
+      endAt: endAt || null
+    }
+  });
+};
+
+/**
+ * 老师删除文档作业
+ * @returns {Promise<{}>}
+ */
+export const delDocAssignment = () => {
+  return request(`${ASSIGNMENT_MODULE}/document/:documentId`);
+};
+
+/**
+ * 老师查看文档作业列表
+ * @returns {Promise<{data:DocumentSerializerType[]}>}
+ */
+export const getDocAssignmentListByTeacher = () => {
+  return request(`${ASSIGNMENT_MODULE}/teacher/course/:courseId/document`);
+};
+
+/**
+ * 学生查看文档作业列表
+ * @returns {Promise<{data:DocumentSerializerType[]}>}
+ */
+export const getDocAssignmentListByStu = () => {
+  return request(`${ASSIGNMENT_MODULE}/student/course/:courseId/document`);
+};
+
+/**
+ * 学生查看单个文档作业下的成绩
+ * @returns {Promise<{data:DocumentResultSerializerType}>}
+ */
+export const getDocResultByDoc = () => {
+  return request(`${ASSIGNMENT_MODULE}/student/document/:documentId/result`);
+};
+
+/**
+ * 老师查看单个文档作业的成绩列表(所有组的)
+ * @returns {Promise<{data:DocumentResultSerializerType}>}
+ */
+export const getDocResultListByDoc = () => {
+  return request(`${ASSIGNMENT_MODULE}/teacher/document/:documentId/result`);
+};

+ 5 - 0
src/assets/scss/app.scss

@@ -61,3 +61,8 @@ $default-margin-between:1.5rem;
     background-color: rgba(0, 0, 0, 0.18) !important;
   }
 }
+
+.tabs a,
+.tabs ul{
+  border-bottom-color: white;
+}

+ 16 - 0
src/components/PageBody/index.vue

@@ -0,0 +1,16 @@
+<template>
+  <div class="page-body"><slot></slot></div>
+</template>
+
+<script>
+export default {
+  name: "PageBody"
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../assets/scss/app";
+.page-body {
+  padding: $default-margin-between;
+}
+</style>

+ 76 - 1
src/components/PageHeader/index.jsx

@@ -1,11 +1,86 @@
+import { mapState } from "vuex";
+import "./index.scss";
+
 export default {
+  name: "PageHeader",
   props: {
     routes: {
       type: Array,
       default: () => []
+    },
+    tabList: {
+      type: Array,
+      default: () => []
     }
   },
+  computed: {
+    ...mapState({
+      course: state => state.course.currentCourse,
+      profile: state => state.user.profile
+    })
+  },
   render() {
-    return <h1>hello</h1>;
+    const { profile, course = {}, routes = [], tabList = [] } = this;
+    const { role = "" } = profile;
+    const prefix = `/course-${role.toLowerCase()}/${course.id}`;
+    const { logo, title, action, content, extraContent } = this.$slots;
+    const showTabs = tabList && tabList.length > 0;
+    console.log(prefix);
+    return (
+      <div class="page-header">
+        <nav class="breadcrumb" aria-label="breadcrumbs">
+          <ul>
+            <li>
+              <router-link to={{ name: "HOME" }}>所有课程</router-link>
+            </li>
+            <li>
+              <router-link to={`${prefix}/dashboard`}>
+                {course.name}
+              </router-link>
+            </li>
+            {routes.map((route, index) => (
+              <li class={index === routes.length - 1 && "is-active"}>
+                <router-link
+                  to={`${prefix}/${route.path}`}
+                  // active-class="is-active"
+                  // tag="li"
+                >
+                  {route.name}
+                </router-link>
+              </li>
+            ))}
+          </ul>
+        </nav>
+        <div class="detail">
+          {logo && <div class="logo">{logo}</div>}
+          <div class="main">
+            <div class="row">
+              {title && <h1 class="title">{title}</h1>}
+              {action && <div class="action">{action}</div>}
+            </div>
+            <div class="row">
+              {content && <div class="content">{content}</div>}
+              {extraContent && <div class="extra-content">{extraContent}</div>}
+            </div>
+          </div>
+        </div>
+        {showTabs && (
+          <div class="tabs">
+            <ul>
+              {tabList.map(tab => (
+                <router-link
+                  active-class="is-active"
+                  exact={tab.exact ? tab.exact : true}
+                  to={`${prefix}/${tab.path}`}
+                  tag="li"
+                >
+                  <a>{tab.name}</a>
+                </router-link>
+              ))}
+            </ul>
+          </div>
+        )}
+      </div>
+    );
   }
 };

+ 105 - 0
src/components/PageHeader/index.scss

@@ -0,0 +1,105 @@
+@import "../../assets/scss/app";
+
+.page-header {
+  background: white;
+  padding: $default-margin-between $default-margin-between*1.5 0 $default-margin-between*1.5;
+  box-shadow: $default-shadow-lighter;
+  .wide {
+    max-width: 1200px;
+    margin: auto;
+  }
+  .detail {
+    display: flex;
+  }
+
+  .row {
+    display: flex;
+    width: 100%;
+  }
+
+  .breadcrumb {
+    margin-bottom: $default-margin-between;
+  }
+
+  .tabs {
+    margin: 0 0 0 -8px;
+
+    :global {
+      // 1px 可以让选中效果显示完成
+      .ant-tabs-bar {
+        border-bottom: none;
+        margin-bottom: 1px;
+      }
+    }
+  }
+
+  .logo {
+    flex: 0 1 auto;
+    margin-right: 16px;
+    padding-top: 1px;
+    > img {
+      width: 28px;
+      height: 28px;
+      border-radius: $default-radius;
+      display: block;
+    }
+  }
+
+  .title {
+    font-size: 20px;
+    font-weight: 500;
+    //color: $default;
+  }
+
+  .action {
+    margin-left: 56px;
+    min-width: 266px;
+
+    :global {
+      .ant-btn-group:not(:last-child),
+      .ant-btn:not(:last-child) {
+        margin-right: 8px;
+      }
+
+      .ant-btn-group > .ant-btn {
+        margin-right: 0;
+      }
+    }
+  }
+
+  .title,
+  .content {
+    flex: auto;
+  }
+
+  .action,
+  .extra-content,
+  .main {
+    flex: 0 1 auto;
+  }
+
+  .main {
+    width: 100%;
+  }
+
+  .title,
+  .action {
+    margin-bottom: $default-margin-between;
+  }
+
+  .logo,
+  .content,
+  .extra-content {
+    margin-bottom: $default-margin-between;
+  }
+
+  .action,
+  .extraContent {
+    text-align: right;
+  }
+
+  .extraContent {
+    margin-left: 88px;
+    min-width: 242px;
+  }
+}

+ 1 - 2
src/layouts/courseLayouts/LayoutStructure.vue

@@ -81,7 +81,6 @@ $top-section-height: 4.25rem;
 
 .router-section {
   /*padding: 24px;*/
-  flex-grow: 1;
   flex: 1;
   /*max-width: 1000px;*/
 }
@@ -152,6 +151,6 @@ $top-section-height: 4.25rem;
 }
 
 .router-part {
-  padding: $default-margin-between;
+  //padding: $default-margin-between;
 }
 </style>

+ 40 - 1
src/views/teacher/Dashboard/index.vue

@@ -1,3 +1,42 @@
 <template>
-  <div></div>
+  <div>
+    <page-header
+      :routes="[
+        { name: '小白大王', path: 'adsfsa' },
+        { name: '小白大fsa王dasfas', path: 'adsfsa/fdsafdsa' }
+      ]"
+      :tab-list="[
+        { name: '小白大王', path: 'dashboard' },
+        { name: '小白大fsa王dasfas', path: 'adsfsa/fdsafdsa' }
+      ]"
+    >
+      <template slot="title">
+        Here might be a page title
+      </template>
+      <template slot="content">
+        Here might be a page title
+      </template>
+      <template slot="extraContent">
+        Here might be a page title
+      </template>
+      <template slot="logo">
+        Here might
+      </template>
+      <template slot="action">
+        Here might be a page title
+      </template>
+    </page-header>
+    <page-body> fdjasoifjdasiofjsa </page-body>
+  </div>
 </template>
+
+<script>
+import PageHeader from "@/components/PageHeader";
+import PageBody from "@/components/PageBody/index";
+export default {
+  components: {
+    PageBody,
+    PageHeader
+  }
+};
+</script>