Browse Source

前端基本完成

靳炳淑 5 years ago
parent
commit
3385cff469

+ 4 - 4
src/api/course.js

@@ -13,7 +13,7 @@ export const getCourseById = payload => {
     {
       "id": 1,
       "name": "软件工程与计算 I",
-      "type": "中级",
+      "type": "中级", 
       "intro": "通过Scheme、Python和Java语言,让你分别体会函数式编程范式、结构化编程范式和面向对象编程范式",
       "picture": "course1.png",
       "school": "南京大学",
@@ -56,7 +56,7 @@ export const getCoursesByKey = payload => {
           "cost": 1,
           "teacherId": 1,
           "teacherName": "刘钦",
-          "bought": false,
+          "bought": true,
           "manageable": false
         },
         {
@@ -67,7 +67,7 @@ export const getCoursesByKey = payload => {
           "picture": "course2.png",
           "school": "南京大学",
           "createTime": "2020-12-20T02:00:00.000+00:00",
-          "cost": 1,
+          "cost": 0,
           "teacherId": 1,
           "teacherName": "刘钦",
           "bought": false,
@@ -215,7 +215,7 @@ export const getBoughtCourses = uid => {
 
 export const getTeacherCourses = uid => {
   // return axios.get(`${COURSE_MODULE}/tid/${uid}`);
-  return Promise(
+  return Promise.resolve(
     [
       {
         "id": 1,

+ 3 - 3
src/api/order.js

@@ -11,7 +11,7 @@ export const createOrder = payload => {
   // return axios.post(`${ORDER_MODULE}/create`, {
   //   courseId, courseName, cost, userId, status
   // });
-  Promise.resolve(
+  return Promise.resolve(
     {
       "code": 0,
       "msg": "当前用户已经购买过此课程,或者有上一笔未支付的订单",
@@ -72,7 +72,7 @@ export const getOrdersByUser = uid => {
         "status": 1
       },
       {
-        "id": 3,
+        "id": 1,
         "cost": 1,
         "courseId": 1,
         "courseName": "软件工程与计算 I",
@@ -81,7 +81,7 @@ export const getOrdersByUser = uid => {
         "status": 1
       },
       {
-        "id": 3,
+        "id": 2,
         "cost": 1,
         "courseId": 1,
         "courseName": "软件工程与计算 I",

+ 5 - 1
src/api/user.js

@@ -7,6 +7,7 @@ import { USER_MODULE } from "./_prefix";
  * @returns 
  */
 export const register = payload => {
+  console.log(payload);
   // const { uname, phone, password, picture, userRole = "STUDENT" } = payload;
   // return axios.post(`${USER_MODULE}/register`, {
   //   uname, phone, password, picture, userRole
@@ -33,6 +34,7 @@ export const register = payload => {
  * @returns 
  */
 export const login = payload => {
+  console.log(payload);
   // const { phone, password } = payload;
   // return axios.post(`${USER_MODULE}/login`, { phone, password });
   return Promise.resolve({
@@ -45,7 +47,7 @@ export const login = payload => {
       "password": null,
       "picture": null,
       "balance": 0,
-      "userRole": "STUDENT",
+      "userRole": "TEACHER",
       "createTime": "2020-12-18T02:00:00.000+00:00"
     }
   });
@@ -57,6 +59,7 @@ export const login = payload => {
  * @returns 
  */
 export const recharge = payload => {
+  console.log(payload);
   // const { userId, value } = payload;
   // return axios.post(`${USER_MODULE}/recharge`, { userId, value });
   return Promise.resolve(
@@ -83,6 +86,7 @@ export const recharge = payload => {
  * @returns 
  */
 export const getUser = uid => {
+  console.log(uid);
   // return axios.get(`${USER_MODULE}/${uid}`);
   return Promise.resolve(
     {

+ 61 - 16
src/components/CourseItem.vue

@@ -12,22 +12,25 @@
       </v-chip>
     </v-card-title>
 
-    <v-card-subtitle> {{ description }} </v-card-subtitle>
+    <v-card-text class="text">
+      {{ text }}
+    </v-card-text>
 
     <v-card-actions>
-      <v-btn text v-show="status !== 2 && status !== -1" @click="handleStudy"
+      <v-btn text v-show="status === 0 || status === 1" @click="handleStudy"
         >学习课程</v-btn
       >
-      <v-btn text v-show="status === 2 || status === -1" @click="handlePeek"
+      <v-btn text v-show="status === -1 || !hasLogin" @click="handlePeek"
         >浏览课程</v-btn
       >
-      <v-btn text v-show="status === 2" @click="buyCourse">购买课程</v-btn>
+      <v-btn text v-show="status === -1" @click="buyCourse">购买课程</v-btn>
     </v-card-actions>
   </v-card>
 </template>
 
 <script lang="ts">
 import Vue from "vue";
+
 export default Vue.extend({
   name: "CourseItem",
   props: {
@@ -36,20 +39,36 @@ export default Vue.extend({
       default: "SubjectName",
     },
     courseId: {
-      type: String,
-      default: "0000",
-    },
-    courseColor: {
-      type: String,
-      default: "#BCAAA4",
+      type: Number,
+      default: 0,
     },
     description: {
       type: String,
       default: "课程简介",
     },
-    status: {
+    type: {
+      type: String,
+      default: "初级",
+    },
+    cost: {
       type: Number,
-      default: 1, // -1: 不显示  0: 免费 1: 已购 2:收费
+      default: 0,
+    },
+    bought: {
+      type: Boolean,
+      default: false,
+    },
+    manageable: {
+      type: Boolean,
+      default: false,
+    },
+    hasLogin: {
+      type: Boolean,
+      default: true,
+    },
+    courseColor: {
+      type: String,
+      default: "#BCAAA4",
     },
   },
   data() {
@@ -60,16 +79,42 @@ export default Vue.extend({
   },
   methods: {
     buyCourse() {
-      this.$emit("buy-course", this.courseName, 50);
+      this.$emit("buy-course", this.courseId, this.courseName, this.cost);
     },
     handleStudy() {
-      this.$router.push("/student/course/001");
+      if (this.hasLogin) {
+        this.$router.push(`/student/course/${this.courseId}`);
+      } else {
+        this.$emit("buy-course", this.courseId, this.courseName, this.cost);
+      }
     },
     handlePeek() {
-      this.$router.push("/student/peek/001");
+      this.$router.push(`/student/peek/${this.courseId}`);
+    },
+  },
+  computed: {
+    text: function() {
+      return this.description.length < 60
+        ? this.description
+        : this.description.substring(0, 60) + "...";
+    },
+
+    // 0 免费  1 已购
+    status: function() {
+      if (this.cost === 0) {
+        return 0;
+      } else if (this.bought) {
+        return 1;
+      }
+      return -1;
     },
   },
 });
 </script>
 
-<style scoped></style>
+<style scoped>
+.text {
+  height: 85px;
+  overflow: hidden;
+}
+</style>

+ 33 - 7
src/components/CourseItemTeacher.vue

@@ -1,14 +1,16 @@
 <template>
-  <v-card :color="courseColor" dark width="280" class="ma-4">
+  <v-card :color="courseColor" dark width="320" class="ma-4 pa-2">
     <v-card-title class="headline">
       {{ courseName }}
     </v-card-title>
 
-    <v-card-subtitle> {{ description }} </v-card-subtitle>
+    <v-card-text class="text">
+      {{ text }}
+    </v-card-text>
 
     <v-card-actions>
       <router-link :to="`/teacher/edit/${courseId}`">
-        <v-btn text>管理课程</v-btn>
+        <v-btn text v-show="manageable">管理课程</v-btn>
       </router-link>
     </v-card-actions>
   </v-card>
@@ -24,16 +26,40 @@ export default Vue.extend({
       default: "SubjectName",
     },
     courseId: {
+      type: Number,
+      default: 0,
+    },
+    description: {
       type: String,
-      default: "0000",
+      default: "课程简介",
+    },
+    type: {
+      type: String,
+      default: "初级",
+    },
+    cost: {
+      type: Number,
+      default: 0,
+    },
+    bought: {
+      type: Boolean,
+      default: false,
+    },
+    manageable: {
+      type: Boolean,
+      default: false,
     },
     courseColor: {
       type: String,
       default: "#BCAAA4",
     },
-    description: {
-      type: String,
-      default: "课程简介",
+  },
+
+  computed: {
+    text: function() {
+      return this.description.length < 60
+        ? this.description
+        : this.description.substring(0, 60) + "...";
     },
   },
 });

+ 3 - 1
src/layouts/defaultLayout.vue

@@ -1,7 +1,9 @@
 <template>
   <div>
     <v-app-bar color="cyan darken-3" dense dark>
-      <v-app-bar-nav-icon></v-app-bar-nav-icon>
+      <v-app-bar-nav-icon @click="direct('/')">
+        <v-icon>mdi-home</v-icon></v-app-bar-nav-icon
+      >
 
       <v-toolbar-title @click="direct('/')" class="cursor">
         Course Learning

+ 3 - 1
src/layouts/studentLayout.vue

@@ -1,7 +1,9 @@
 <template>
   <div>
     <v-app-bar color="deep-purple lighten-3" dense dark>
-      <v-app-bar-nav-icon></v-app-bar-nav-icon>
+      <v-app-bar-nav-icon @click="direct('/student')">
+        <v-icon>mdi-home</v-icon></v-app-bar-nav-icon
+      >
 
       <v-toolbar-title @click="direct('/student')" class="cursor">
         Course Learning Student

+ 3 - 1
src/layouts/teacherLayout.vue

@@ -1,7 +1,9 @@
 <template>
   <div>
     <v-app-bar color="indigo lighten-1" dense dark>
-      <v-app-bar-nav-icon></v-app-bar-nav-icon>
+      <v-app-bar-nav-icon @click="direct('/teacher')">
+        <v-icon>mdi-home</v-icon></v-app-bar-nav-icon
+      >
 
       <v-toolbar-title @click="direct('/teacher')" class="cursor"
         >Course Learning Teacher</v-toolbar-title

+ 6 - 0
src/router/index.js

@@ -15,6 +15,7 @@ import CoursePeek from "@/views/student/CoursePeek.vue";
 
 import DefaultLayout from "@/layouts/defaultLayout.vue";
 import Login from "@/views/Login.vue";
+import Register from "@/views/Register.vue";
 import Home from "@/views/Home.vue";
 
 Vue.use(VueRouter);
@@ -34,6 +35,11 @@ const routes = [
         path: "login",
         name: "Login",
         component: Login,
+      },
+      {
+        path: "register",
+        name: "Register",
+        component: Register,
       }
     ]
   },

+ 192 - 88
src/views/Home.vue

@@ -1,116 +1,220 @@
 <template>
   <div>
     <v-container class="ma-8 pa-4">
-      <v-row>
-        <v-chip class="ma-2" color="primary">
-          热门课程
-        </v-chip>
-      </v-row>
-      <v-row class="mb-4">
-        <course-item
-          v-for="(item, index) in courseList1"
-          :key="index"
-          :courseName="item.name"
-          :description="item.description"
-          :status="-1"
-          :course-color="colorList[index % colorList.length]"
-          @buy-course="showDialog"
-        ></course-item>
-      </v-row>
-      <v-row class="mb-10">
+      <template>
+        <v-card>
+          <v-tabs color="deep-purple accent-4" right v-model="currentTab">
+            <v-tab>初级课程</v-tab>
+            <v-tab>中级课程</v-tab>
+            <v-tab>高级课程</v-tab>
+
+            <v-tab-item v-for="c in [primary, medium, advanced]" :key="c.name">
+              <v-container fluid>
+                <v-row class="ma-4">
+                  <course-item
+                    cols="12"
+                    md="4"
+                    v-for="course in c.list"
+                    :key="course.id"
+                    :courseName="course.name"
+                    :courseId="course.id"
+                    :description="course.intro"
+                    :type="course.type"
+                    :cost="course.cost"
+                    :bought="false"
+                    :manageable="course.manageable"
+                    :course-color="colorList[course.id % colorList.length]"
+                    :hasLogin="false"
+                    @buy-course="showDialog"
+                  >
+                  </course-item>
+                </v-row>
+                <v-pagination
+                  v-model="c.currentPage"
+                  :length="c.totalPage"
+                  circle
+                ></v-pagination>
+              </v-container>
+            </v-tab-item>
+          </v-tabs>
+        </v-card>
+      </template>
+
+      <v-row class="ma-12">
         <v-divider></v-divider>
       </v-row>
-      <v-row>
-        <v-chip class="ma-2" color="primary">
-          课程分类
-        </v-chip>
-      </v-row>
-      <v-row class="mt-10 mr-12">
-        <v-select :items="types" filled label="课程检索"></v-select>
+
+      <v-row class="mt-10">
+        <v-col cols="5">
+          <v-text-field
+            v-model="searchText"
+            outlined
+            label="搜索课程"
+            append-icon="mdi-magnify"
+          ></v-text-field>
+        </v-col>
       </v-row>
       <v-row>
         <course-item
-          v-for="(item, index) in courseList"
-          :key="index"
-          :courseName="item.name"
-          :description="item.description"
-          :status="-1"
-          :course-color="colorList[index % colorList.length]"
+          cols="12"
+          md="4"
+          v-for="course in searchCourse"
+          :key="course.id"
+          :courseName="course.name"
+          :courseId="course.id"
+          :description="course.intro"
+          :type="course.type"
+          :cost="course.cost"
+          :bought="false"
+          :manageable="course.manageable"
+          :course-color="colorList[course.id % colorList.length]"
+          :hasLogin="false"
           @buy-course="showDialog"
-        ></course-item>
+        >
+        </course-item>
+      </v-row>
+      <v-row>
+        <v-pagination
+          v-model="searchCurrentPage"
+          :length="searchTotalPage"
+          circle
+        ></v-pagination>
       </v-row>
     </v-container>
+
+    <!-- 购买结果 -->
+    <v-dialog v-model="dialog" width="500">
+      <v-card>
+        <v-card-title>请先登录</v-card-title>
+
+        <v-card-text>
+          登录后可以学习更多课程哦
+        </v-card-text>
+
+        <v-divider></v-divider>
+
+        <v-card-actions>
+          <v-spacer></v-spacer>
+          <v-btn color="primary" text @click="dialog = false">
+            确认
+          </v-btn>
+        </v-card-actions>
+      </v-card>
+    </v-dialog>
   </div>
 </template>
 
 <script>
 import CourseItem from "@/components/CourseItem.vue";
+import { getCoursesByType, getCoursesByKey } from "@/api/course";
 
 export default {
-  name: "StudentCourseList",
+  name: "Home",
   components: {
     CourseItem,
   },
   data() {
     return {
-      types: ["软件工程", "算法", "网络"],
+      dialog: false,
+      dialog2: false,
+      dialog3: false,
+
       colorList: ["#26A69A", "#00B0FF", "#5C6BC0", "#FFB300", "#E57373"],
-      courseList1: [
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-      ],
-      courseList: [
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-      ],
+      currentTab: 0, // 0 1 2
+      primary: {
+        name: "初级",
+        totalPage: 1,
+        currentPage: 1,
+        list: [],
+      },
+      medium: {
+        name: "中级",
+        totalPage: 1,
+        currentPage: 1,
+        list: [],
+      },
+      advanced: {
+        name: "高级",
+        totalPage: 1,
+        currentPage: 1,
+        list: [],
+      },
+      searchText: "",
+      searchCourse: [],
+      searchTotalPage: 1,
+      searchCurrentPage: 1,
     };
   },
-  methods: {},
+  watch: {
+    "primary.currentPage": {
+      handler: function(val) {
+        this.handleGetCoursesByType(val, "primary");
+      },
+    },
+
+    "medium.currentPage": {
+      handler: function(val) {
+        this.handleGetCoursesByType(val, "medium");
+      },
+    },
+
+    "advance.currentPage": {
+      handler: function(val) {
+        this.handleGetCoursesByType(val, "advanced");
+      },
+    },
+
+    searchCurrentPage: function() {
+      this.handleSearchCourse();
+    },
+
+    searchText: function() {
+      this.handleSearchCourse();
+    },
+  },
+  methods: {
+    showDialog() {
+      this.dialog = true;
+    },
+
+    handleBuyCourse() {
+      Promise.resolve(1).then((res) => {
+        this.dialog = false;
+        if (res) {
+          this.dialog2 = true;
+        } else {
+          this.dialog3 = true;
+        }
+      });
+    },
+
+    handleGetCoursesByType(page, type) {
+      const typeMap = { primary: "初级", medium: "中级", advanced: "高级" };
+      getCoursesByType({ uid: "", type: typeMap[type], page }).then((res) => {
+        console.log(res);
+        this[type].totalPage = res.total;
+        this[type].list = res.list;
+      });
+    },
+
+    handleSearchCourse() {
+      getCoursesByKey({
+        uid: "",
+        key: this.searchText,
+        page: this.searchCurrentPage,
+      }).then((res) => {
+        console.log(res);
+        this.searchCourse = res.list;
+        this.searchTotalPage = res.total;
+      });
+    },
+  },
+
+  mounted() {
+    ["primary", "medium", "advanced"].forEach((t) => {
+      this.handleGetCoursesByType(1, t);
+    });
+    this.handleSearchCourse();
+  },
 };
 </script>

+ 79 - 13
src/views/Login.vue

@@ -1,38 +1,104 @@
 <template>
   <div>
     <v-container class="pl-16 pr-16">
-      <form class="pa-12 grey lighten-5 mt-8">
-        <v-text-field v-model="username" label="用户名"></v-text-field>
+      <!-- alert -->
+      <v-alert
+        class="dialog"
+        outlined
+        type="success"
+        text
+        v-show="showSuccessDialog"
+        transition="scroll-y-transition"
+      >
+        登录成功,欢迎{{ username }} !
+      </v-alert>
+      <!-- alert -->
+      <v-alert
+        class="dialog"
+        outlined
+        type="warning"
+        text
+        v-show="showFailDialog"
+        transition="scroll-y-transition"
+      >
+        {{ msg }}
+      </v-alert>
+      <form class="pa-12 grey lighten-5 mt-10 form">
+        <v-text-field v-model="phone" label="手机号码"></v-text-field>
         <v-text-field v-model="password" label="密码"></v-text-field>
-        <v-select v-model="role" :items="roles" label="用户角色"> </v-select>
         <v-btn class="ml-0 mt-8 info" @click="handleLogin">
-          登录/注册
+          登录
         </v-btn>
       </form>
+      <v-btn
+        text
+        color="primary"
+        class="float-right ma-4"
+        @click="
+          () => {
+            this.$router.push('/register');
+          }
+        "
+      >
+        去注册
+      </v-btn>
     </v-container>
   </div>
 </template>
 
 <script>
+import { login } from "@/api/user";
+
 export default {
   name: "Login",
   data() {
     return {
-      username: "",
+      phone: "",
       password: "",
-      role: "student",
-      roles: ["teacher", "student"],
+      showSuccessDialog: false,
+      showFailDialog: false,
+      msg: "",
+      username: "",
     };
   },
   methods: {
     handleLogin() {
-      if (this.role === "student") {
-        this.$router.push("/student");
-      }
-      if (this.role === "teacher") {
-        this.$router.push("/teacher");
-      }
+      login({ phone: this.phone, password: this.password }).then((res) => {
+        console.log(res);
+        if (res.code === 1) {
+          window.localStorage.setItem("userId", res.data.id);
+          window.localStorage.setItem("userPhone", res.data.phone);
+          window.localStorage.setItem("username", res.data.uname);
+          window.localStorage.setItem("userRole", res.data.userRole);
+          this.username = res.data.uname;
+          this.showSuccessDialog = true;
+          if (res.data.userRole === "STUDENT") {
+            setTimeout(() => {
+              this.$router.push("/student");
+            }, 800);
+          }
+          if (res.data.userRole === "TEACHER") {
+            setTimeout(() => {
+              this.$router.push("/teacher");
+            }, 800);
+          }
+        } else {
+          this.showFailDialog = true;
+          this.msg = res.msg;
+        }
+      });
     },
   },
 };
 </script>
+
+<style scoped>
+.dialog {
+  position: fixed;
+  left: 50%;
+}
+.form {
+  position: relative;
+  top: 50px;
+}
+</style>

+ 108 - 0
src/views/Register.vue

@@ -0,0 +1,108 @@
+<template>
+  <div>
+    <v-container class="pl-16 pr-16">
+      <!-- alert -->
+      <v-alert
+        class="dialog"
+        outlined
+        type="success"
+        text
+        v-show="showSuccessDialog"
+        transition="scroll-y-transition"
+      >
+        注册成功,欢迎{{ username }} !
+      </v-alert>
+      <!-- alert -->
+      <v-alert
+        class="dialog"
+        outlined
+        type="warning"
+        text
+        v-show="showFailDialog"
+        transition="scroll-y-transition"
+      >
+        {{ msg }}
+      </v-alert>
+      <form class="pa-12 grey lighten-5 mt-10 form">
+        <v-text-field v-model="username" label="用户名"></v-text-field>
+        <v-text-field v-model="phone" label="手机号码"></v-text-field>
+        <v-text-field
+          v-model="password"
+          type="password"
+          label="密码"
+        ></v-text-field>
+        <v-select v-model="userRole" :items="roles" label="用户角色"></v-select>
+        <v-btn class="ml-0 mt-8 info" @click="handleRegister">
+          注册
+        </v-btn>
+      </form>
+      <v-btn
+        text
+        color="primary"
+        class="float-right ma-4"
+        @click="
+          () => {
+            this.$router.push('/login');
+          }
+        "
+      >
+        去登录
+      </v-btn>
+    </v-container>
+  </div>
+</template>
+
+<script>
+import { register } from "@/api/user";
+
+export default {
+  name: "Register",
+  data() {
+    return {
+      username: "",
+      phone: "",
+      password: "",
+      userRole: "student",
+      showSuccessDialog: false,
+      showFailDialog: false,
+      msg: "",
+      roles: ["teacher", "student"],
+    };
+  },
+  methods: {
+    handleRegister() {
+      register({
+        uname: this.username,
+        phone: this.phone,
+        password: this.password,
+        picture: null,
+        userRole: this.userRole.toUpperCase(),
+      }).then((res) => {
+        console.log(res);
+        if (res.code === 1) {
+          window.localStorage.setItem("userId", res.data.id);
+          window.localStorage.setItem("userPhone", res.data.phone);
+          window.localStorage.setItem("username", res.data.uname);
+          window.localStorage.setItem("userRole", res.data.userRole);
+          this.username = res.data.uname;
+          this.showSuccessDialog = true;
+        } else {
+          this.showFailDialog = true;
+          this.msg = res.msg;
+        }
+      });
+    },
+  },
+};
+</script>
+
+<style scoped>
+.dialog {
+  position: fixed;
+  left: 50%;
+}
+.form {
+  position: relative;
+  top: 50px;
+}
+</style>

+ 169 - 105
src/views/student/CourseList.vue

@@ -1,38 +1,82 @@
 <template>
   <div>
     <v-container class="ma-8 pa-4">
-      <v-row>
-        <v-chip class="ma-2" color="primary">
-          热门课程
-        </v-chip>
-      </v-row>
-      <v-row class="mb-4">
-        <course-item
-          v-for="(item, index) in courseList1"
-          :key="index"
-          :courseName="item.name"
-          :description="item.description"
-          :status="item.status"
-          :course-color="colorList[index % colorList.length]"
-          @buy-course="showDialog"
-        ></course-item>
-      </v-row>
-      <v-row class="mb-10">
+      <template>
+        <v-card>
+          <v-tabs color="deep-purple accent-4" right v-model="currentTab">
+            <v-tab>初级课程</v-tab>
+            <v-tab>中级课程</v-tab>
+            <v-tab>高级课程</v-tab>
+
+            <v-tab-item v-for="c in [primary, medium, advanced]" :key="c.name">
+              <v-container fluid>
+                <v-row class="ma-4">
+                  <course-item
+                    cols="12"
+                    md="4"
+                    v-for="course in c.list"
+                    :key="course.id"
+                    :courseName="course.name"
+                    :courseId="course.id"
+                    :description="course.intro"
+                    :type="course.type"
+                    :cost="course.cost"
+                    :bought="course.bought"
+                    :manageable="course.manageable"
+                    :course-color="colorList[course.id % colorList.length]"
+                    @buy-course="showDialog"
+                  >
+                  </course-item>
+                </v-row>
+                <v-pagination
+                  v-model="c.currentPage"
+                  :length="c.totalPage"
+                  circle
+                ></v-pagination>
+              </v-container>
+            </v-tab-item>
+          </v-tabs>
+        </v-card>
+      </template>
+
+      <v-row class="ma-12">
         <v-divider></v-divider>
       </v-row>
-      <v-row>
-        <v-select :items="types" filled label="课程检索"></v-select>
+
+      <v-row class="mt-10">
+        <v-col cols="5">
+          <v-text-field
+            v-model="searchText"
+            outlined
+            label="搜索课程"
+            append-icon="mdi-magnify"
+          ></v-text-field>
+        </v-col>
       </v-row>
       <v-row>
         <course-item
-          v-for="(item, index) in courseList"
-          :key="index"
-          :courseName="item.name"
-          :description="item.description"
-          :status="item.status"
-          :course-color="colorList[index % colorList.length]"
+          cols="12"
+          md="4"
+          v-for="course in searchCourse"
+          :key="course.id"
+          :courseName="course.name"
+          :courseId="course.id"
+          :description="course.intro"
+          :type="course.type"
+          :cost="course.cost"
+          :bought="course.bought"
+          :manageable="course.manageable"
+          :course-color="colorList[course.id % colorList.length]"
           @buy-course="showDialog"
-        ></course-item>
+        >
+        </course-item>
+      </v-row>
+      <v-row>
+        <v-pagination
+          v-model="searchCurrentPage"
+          :length="searchTotalPage"
+          circle
+        ></v-pagination>
       </v-row>
     </v-container>
 
@@ -67,7 +111,7 @@
         <v-card-title>购买课程</v-card-title>
 
         <v-card-text>
-          购买成功!
+          {{ msg }}
         </v-card-text>
 
         <v-divider></v-divider>
@@ -80,30 +124,13 @@
         </v-card-actions>
       </v-card>
     </v-dialog>
-    <!-- 购买结果 -->
-    <v-dialog v-model="dialog3" width="500">
-      <v-card>
-        <v-card-title>购买课程</v-card-title>
-
-        <v-card-text>
-          余额不足。
-        </v-card-text>
-
-        <v-divider></v-divider>
-
-        <v-card-actions>
-          <v-spacer></v-spacer>
-          <v-btn color="primary" text @click="dialog3 = false">
-            确认
-          </v-btn>
-        </v-card-actions>
-      </v-card>
-    </v-dialog>
   </div>
 </template>
 
 <script>
 import CourseItem from "@/components/CourseItem.vue";
+import { getCoursesByType, getCoursesByKey } from "@/api/course";
+import { createOrder } from "@/api/order";
 
 export default {
   name: "StudentCourseList",
@@ -114,79 +141,116 @@ export default {
     return {
       dialog: false,
       dialog2: false,
-      dialog3: false,
+      msg: "",
+      currentCourseId: 0,
       currentCourseName: "",
-      currentCoursePrice: 50,
+      currentCoursePrice: 0,
       colorList: ["#26A69A", "#00B0FF", "#5C6BC0", "#FFB300", "#E57373"],
-      types: ["软件工程", "算法", "网络"],
-      courseList1: [
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: 2,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: 0,
-        },
-      ],
-      courseList: [
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: 1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: 0,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: 1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: 2,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: 0,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: 1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: 0,
-        },
-      ],
+      currentTab: 0, // 0 1 2
+      primary: {
+        name: "初级",
+        totalPage: 1,
+        currentPage: 1,
+        list: [],
+      },
+      medium: {
+        name: "中级",
+        totalPage: 1,
+        currentPage: 1,
+        list: [],
+      },
+      advanced: {
+        name: "高级",
+        totalPage: 1,
+        currentPage: 1,
+        list: [],
+      },
+      searchText: "",
+      searchCourse: [],
+      searchTotalPage: 1,
+      searchCurrentPage: 1,
     };
   },
+  watch: {
+    "primary.currentPage": {
+      handler: function(val) {
+        this.handleGetCoursesByType(val, "primary");
+      },
+    },
+
+    "medium.currentPage": {
+      handler: function(val) {
+        this.handleGetCoursesByType(val, "medium");
+      },
+    },
+
+    "advance.currentPage": {
+      handler: function(val) {
+        this.handleGetCoursesByType(val, "advanced");
+      },
+    },
+
+    searchCurrentPage: function() {
+      this.handleSearchCourse();
+    },
+
+    searchText: function() {
+      this.handleSearchCourse();
+    },
+  },
   methods: {
-    showDialog(courseName, coursePrice) {
+    showDialog(courseId, courseName, coursePrice) {
+      this.currentCourseId = courseId;
       this.currentCourseName = courseName;
       this.currentCoursePrice = coursePrice;
       this.dialog = true;
     },
 
     handleBuyCourse() {
-      Promise.resolve(1).then((res) => {
+      const uid = window.localStorage.getItem("userId");
+      createOrder({
+        courseId: this.currentCourseId,
+        courseName: this.currentCourseName,
+        cost: this.currentCoursePrice,
+        userId: uid,
+        status: 2,
+      }).then((res) => {
         this.dialog = false;
-        if (res) {
-          this.dialog2 = true;
-        } else {
-          this.dialog3 = true;
-        }
+        this.msg =
+          (res.code === 1 ? "「购买成功」!" : "「购买失败」!") + res.msg;
+        this.dialog2 = true;
+      });
+    },
+
+    handleGetCoursesByType(page, type) {
+      const uid = window.localStorage.getItem("userId");
+      const typeMap = { primary: "初级", medium: "中级", advanced: "高级" };
+      getCoursesByType({ uid, type: typeMap[type], page }).then((res) => {
+        console.log(res);
+        this[type].totalPage = res.total;
+        this[type].list = res.list;
       });
     },
+
+    handleSearchCourse() {
+      const uid = window.localStorage.getItem("userId");
+      getCoursesByKey({
+        uid,
+        key: this.searchText,
+        page: this.searchCurrentPage,
+      }).then((res) => {
+        console.log(res);
+        this.searchCourse = res.list;
+        this.searchTotalPage = res.total;
+      });
+    },
+  },
+
+  mounted() {
+    ["primary", "medium", "advanced"].forEach((t) => {
+      this.handleGetCoursesByType(1, t);
+    });
+    this.handleSearchCourse();
   },
 };
 </script>

+ 73 - 17
src/views/student/CoursePeek.vue

@@ -1,40 +1,96 @@
 <template>
   <div>
-    <v-container class="ma-8 pa-4">
+    <v-container class="pl-16 pr-16">
       <v-row>
-        课程内容浏览
+        <v-col>
+          <form class="pa-12 grey lighten-5 mt-8">
+            <v-text-field
+              v-model="courseInfo.name"
+              label="课程名称"
+              readonly
+            ></v-text-field>
+            <v-text-field
+              v-model="courseInfo.id"
+              label="课程ID"
+              readonly
+            ></v-text-field>
+            <v-select
+              :items="types"
+              v-model="courseInfo.type"
+              label="课程分类"
+              readonly
+            ></v-select>
+            <v-textarea
+              v-model="courseInfo.intro"
+              label="课程简介"
+              readonly
+            ></v-textarea>
+            <v-text-field
+              v-model="courseInfo.school"
+              label="所属学校"
+              readonly
+            ></v-text-field>
+            <v-text-field
+              v-model="courseInfo.createTime"
+              label="创建时间"
+              readonly
+            ></v-text-field>
+            <v-text-field
+              v-model="courseInfo.teacherName"
+              label="创建教师"
+              readonly
+            ></v-text-field>
+            <v-switch
+              v-model="isCharge"
+              :label="`是否为付费课程: ${isCharge.toString()}`"
+              readonly
+            ></v-switch>
+            <v-text-field
+              v-show="isCharge"
+              v-model="courseInfo.cost"
+              label="课程价格(元)"
+              readonly
+            ></v-text-field>
+          </form>
+        </v-col>
       </v-row>
     </v-container>
   </div>
 </template>
 
 <script>
+import { getCourseById } from "@/api/course";
+
 export default {
   name: "CoursePeek",
 
   data() {
     return {
-      name: "软件工程",
-      description: "介绍软件工程的方法以及实践",
+      courseInfo: {},
+      types: ["初级", "中级", "高级"],
     };
   },
-  methods: {
-    showDialog(courseName, coursePrice) {
-      this.currentCourseName = courseName;
-      this.currentCoursePrice = coursePrice;
-      this.dialog = true;
+
+  computed: {
+    isCharge: function() {
+      return this.courseInfo.cost !== 0;
     },
+  },
 
-    handleBuyCourse() {
-      Promise.resolve(1).then((res) => {
-        this.dialog = false;
-        if (res) {
-          this.dialog2 = true;
-        } else {
-          this.dialog3 = true;
-        }
+  methods: {
+    loadCourse() {
+      const { courseId } = this.$route.params;
+      const uid = window.localStorage.getItem("userId");
+      getCourseById({ uid, courseId }).then((res) => {
+        this.courseInfo = res;
       });
     },
   },
+
+  mounted() {
+    this.loadCourse();
+  },
 };
 </script>
+
+<style scoped></style>

+ 258 - 16
src/views/student/CourseStudy.vue

@@ -1,40 +1,282 @@
 <template>
   <div>
-    <v-container class="ma-8 pa-4">
+    <v-container class="pl-16 pr-16">
+      <!-- alert-->
+      <v-alert
+        class="alert"
+        outlined
+        type="success"
+        text
+        v-show="showAlert"
+        transition="scroll-y-transition"
+      >
+        {{ alertMsg }}
+      </v-alert>
+
+      <!-- dialog-->
+      <template>
+        <v-row justify="center">
+          <v-dialog v-model="dialog" persistent max-width="600px">
+            <v-card>
+              <v-card-title>
+                <span class="headline">课件详情</span>
+              </v-card-title>
+              <v-card-text>
+                <v-container>
+                  <v-row>
+                    <v-col cols="12" sm="6" md="4">
+                      <v-text-field
+                        label="文件类型"
+                        v-model="currentFile.fileType"
+                        readonly
+                      ></v-text-field>
+                    </v-col>
+                    <v-col cols="12" sm="6" md="4">
+                      <v-text-field
+                        label="文件大小"
+                        v-model="currentFile.fileSize"
+                        readonly
+                      ></v-text-field>
+                    </v-col>
+                    <v-col cols="12">
+                      <v-text-field
+                        label="文件名"
+                        v-model="currentFile.fileName"
+                        readonly
+                      ></v-text-field>
+                    </v-col>
+                    <v-col cols="12">
+                      <v-text-field
+                        label="课件标题"
+                        v-model="currentFile.title"
+                        readonly
+                      ></v-text-field>
+                    </v-col>
+                    <v-col cols="12" sm="6">
+                      <v-select
+                        :items="[true, false]"
+                        label="是否免费"
+                        v-model="currentFile.freeFlag"
+                        readonly
+                      ></v-select>
+                    </v-col>
+                    <v-col cols="12" sm="6">
+                      <v-select
+                        :items="[true, false]"
+                        label="是否可下载"
+                        v-model="currentFile.downloadFlag"
+                        readonly
+                      ></v-select>
+                    </v-col>
+                    <v-col cols="12" sm="6">
+                      <v-select
+                        :items="[true, false]"
+                        label="是否有权查看"
+                        v-model="currentFile.availableFlag"
+                        readonly
+                      ></v-select>
+                    </v-col>
+                  </v-row>
+                </v-container>
+              </v-card-text>
+              <v-card-actions>
+                <v-spacer></v-spacer>
+                <v-btn color="blue darken-1" text @click="dialog = false">
+                  Close
+                </v-btn>
+                <v-btn
+                  v-show="currentFile.downloadFlag"
+                  color="blue darken-1"
+                  text
+                  @click="handleDownloadFile"
+                >
+                  下载课件
+                </v-btn>
+              </v-card-actions>
+            </v-card>
+          </v-dialog>
+        </v-row>
+      </template>
+
       <v-row>
-        课程内容学习
+        <v-col>
+          <v-card max-width="600" class="mx-auto mt-8">
+            <v-toolbar color="indigo lighten-2" dark>
+              <v-app-bar-nav-icon></v-app-bar-nav-icon>
+              <v-toolbar-title>下载课件</v-toolbar-title>
+              <v-spacer></v-spacer>
+            </v-toolbar>
+
+            <v-list subheader two-line>
+              <v-list-item v-for="file in files" :key="file.title">
+                <v-list-item-avatar>
+                  <v-icon class="grey lighten-1" dark>
+                    mdi-file
+                  </v-icon>
+                </v-list-item-avatar>
+
+                <v-list-item-content>
+                  <v-list-item-title v-text="file.title"></v-list-item-title>
+
+                  <v-list-item-subtitle
+                    v-text="file.fileSize"
+                  ></v-list-item-subtitle>
+                </v-list-item-content>
+
+                <v-list-item-action>
+                  <v-btn icon>
+                    <v-icon
+                      color="grey lighten-1"
+                      @click="showFileDialog(file.id)"
+                      >mdi-more</v-icon
+                    >
+                  </v-btn>
+                </v-list-item-action>
+              </v-list-item>
+            </v-list>
+          </v-card>
+        </v-col>
+        <v-col>
+          <form class="pa-12 grey lighten-5 mt-8">
+            <v-text-field
+              v-model="courseInfo.name"
+              label="课程名称"
+              readonly
+            ></v-text-field>
+            <v-text-field
+              v-model="courseInfo.id"
+              label="课程ID"
+              readonly
+            ></v-text-field>
+            <v-select
+              :items="types"
+              v-model="courseInfo.type"
+              label="课程分类"
+              readonly
+            ></v-select>
+            <v-textarea
+              v-model="courseInfo.intro"
+              label="课程简介"
+              readonly
+            ></v-textarea>
+            <v-text-field
+              v-model="courseInfo.school"
+              label="所属学校"
+              readonly
+            ></v-text-field>
+            <v-text-field
+              v-model="courseInfo.createTime"
+              label="创建时间"
+              readonly
+            ></v-text-field>
+            <v-text-field
+              v-model="courseInfo.teacherName"
+              label="创建教师"
+              readonly
+            ></v-text-field>
+            <v-switch
+              v-model="isCharge"
+              :label="`是否为付费课程: ${isCharge.toString()}`"
+              readonly
+            ></v-switch>
+            <v-text-field
+              v-show="isCharge"
+              v-model="courseInfo.cost"
+              label="课程价格(元)"
+              readonly
+            ></v-text-field>
+          </form>
+        </v-col>
       </v-row>
     </v-container>
   </div>
 </template>
 
 <script>
+import { getCourseWareByCourse } from "@/api/courseWare";
+import { getCourseById } from "@/api/course";
+
 export default {
-  name: "CourseStudy",
+  name: "CourseEdit",
 
   data() {
     return {
-      name: "软件工程",
-      description: "介绍软件工程的方法以及实践",
+      courseInfo: {},
+      types: ["初级", "中级", "高级"],
+      msg: "",
+      files: [],
+      showAlert: false,
+      alertMsg: "",
+      dialog: false,
+      currentFile: {
+        id: 0,
+        courseId: 0,
+        number: 0,
+        title: "",
+        fileName: "",
+        fileType: "",
+        fileSize: "",
+        freeFlag: false,
+        downloadFlag: true,
+        availableFlag: false,
+        uploadTime: "",
+      },
     };
   },
+
+  computed: {
+    isCharge: function() {
+      return this.courseInfo.cost !== 0;
+    },
+  },
+
   methods: {
-    showDialog(courseName, coursePrice) {
-      this.currentCourseName = courseName;
-      this.currentCoursePrice = coursePrice;
+    refreshFile() {
+      const { courseId } = this.$route.params;
+      const uid = window.localStorage.getItem("userId");
+      getCourseWareByCourse({ courseId, uid }).then((res) => {
+        this.files = res || [];
+      });
+    },
+
+    loadCourse() {
+      const { courseId } = this.$route.params;
+      const uid = window.localStorage.getItem("userId");
+      getCourseById({ uid, courseId }).then((res) => {
+        this.courseInfo = res;
+      });
+    },
+
+    showFileDialog(courseWareId) {
+      const currentFile = this.files.find((f) => f.id === courseWareId);
+      console.log(currentFile);
+      this.currentFile = currentFile;
       this.dialog = true;
     },
 
-    handleBuyCourse() {
-      Promise.resolve(1).then((res) => {
+    handleDownloadFile() {
+      setTimeout(() => {
+        this.alertMsg = "下载成功!";
+        this.showAlert = true;
         this.dialog = false;
-        if (res) {
-          this.dialog2 = true;
-        } else {
-          this.dialog3 = true;
-        }
-      });
+      }, 500);
+      setTimeout(() => {
+        this.showAlert = false;
+      }, 1500);
     },
   },
+
+  mounted() {
+    this.refreshFile();
+    this.loadCourse();
+  },
 };
 </script>
+
+<style scoped>
+.alert {
+  position: fixed;
+  left: 50%;
+  top: 100px;
+}
+</style>

+ 23 - 58
src/views/student/HistoryOrder.vue

@@ -2,18 +2,20 @@
   <div>
     <v-container class="ma-8 pa-4">
       <v-card
-        v-for="(course, index) in courseList"
-        :key="index"
-        :color="colorList[index % colorList.length]"
+        v-for="o in orderList"
+        :key="o.id"
+        :color="colorList[o.id % colorList.length]"
         class="ma-8 pa-4"
       >
         <v-card-title class="headline">
-          {{ course.name }}
+          {{ o.courseName }}
         </v-card-title>
 
-        <v-card-subtitle> {{ course.description }} </v-card-subtitle>
-        <v-card-actions>
-          <v-btn text @click="handleStudy">学习课程</v-btn>
+        <v-card-subtitle class="mt-1">
+          购买时间: {{ o.createTime }}
+        </v-card-subtitle>
+        <v-card-actions class="pa-0 pl-2">
+          <v-btn text @click="handleStudy(o.courseId)">学习课程</v-btn>
         </v-card-actions>
       </v-card>
     </v-container>
@@ -21,9 +23,10 @@
 </template>
 
 <script>
-export default {
-  name: "StudentCourseList",
+import { getOrdersByUser } from "@/api/order";
 
+export default {
+  name: "HistoryOrder",
   data() {
     return {
       colorList: [
@@ -34,60 +37,22 @@ export default {
         "#B39DDB",
         "#EF9A9A",
       ],
-      courseList: [
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          price: 20,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          price: 20,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          price: 20,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          price: 20,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          price: 20,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          price: 20,
-        },
-      ],
+      orderList: [],
     };
   },
+
   methods: {
-    showDialog(courseName, coursePrice) {
-      this.currentCourseName = courseName;
-      this.currentCoursePrice = coursePrice;
-      this.dialog = true;
+    handleStudy(courseId) {
+      this.$router.push(`/student/course/${courseId}`);
     },
+  },
 
-    handleBuyCourse() {
-      Promise.resolve(1).then((res) => {
-        this.dialog = false;
-        if (res) {
-          this.dialog2 = true;
-        } else {
-          this.dialog3 = true;
-        }
-      });
-    },
-    handleStudy() {
-      this.$router.push("/student/course/001");
-    },
+  mounted() {
+    const uid = window.localStorage.getItem("userId");
+    getOrdersByUser(uid).then((res) => {
+      console.log(res);
+      this.orderList = res || [];
+    });
   },
 };
 </script>

+ 112 - 8
src/views/student/UserCenter.vue

@@ -1,32 +1,136 @@
 <template>
   <div>
+    <!-- alert -->
+    <v-alert
+      class="alert"
+      outlined
+      type="success"
+      text
+      v-show="showAlert"
+      transition="scroll-y-transition"
+    >
+      {{ msg }}
+    </v-alert>
+    <template>
+      <v-dialog v-model="dialog" max-width="400">
+        <v-card>
+          <v-card-title class="headline">
+            账户充值
+          </v-card-title>
+          <v-text-field
+            v-model="value"
+            label="充值金额"
+            class="pa-6"
+          ></v-text-field>
+
+          <v-card-actions>
+            <v-spacer></v-spacer>
+
+            <v-btn color="green darken-1" text @click="handleRecharge">
+              充值
+            </v-btn>
+
+            <v-btn color="green darken-1" text @click="dialog = false">
+              取消
+            </v-btn>
+          </v-card-actions>
+        </v-card>
+      </v-dialog>
+    </template>
+
     <v-container class="pl-16 pr-16">
       <form class="pa-12 grey lighten-5 mt-8">
-        <v-text-field v-model="username" label="用户名" disabled></v-text-field>
-
+        <v-text-field v-model="userInfo.id" label="ID" readonly></v-text-field>
+        <v-text-field
+          v-model="userInfo.uname"
+          label="用户名"
+          readonly
+        ></v-text-field>
+        <v-text-field
+          v-model="userInfo.phone"
+          label="手机号"
+          readonly
+        ></v-text-field>
         <v-text-field
-          v-model="account"
+          v-model="userInfo.userRole"
+          label="用户角色"
+          readonly
+        ></v-text-field>
+        <v-text-field
+          v-model="userInfo.createTime"
+          label="注册时间"
+          readonly
+        ></v-text-field>
+        <v-text-field
+          v-model="userInfo.balance"
           label="账户余额"
-          disabled
+          readonly
         ></v-text-field>
+        <v-btn color="primary" dark @click.stop="dialog = true">
+          充值
+        </v-btn>
       </form>
     </v-container>
   </div>
 </template>
 
 <script>
-// @ is an alias to /src
+import { getUser, recharge } from "@/api/user";
 
 export default {
   name: "UserCenter",
 
   data() {
     return {
-      username: "kate",
-      account: 1000,
+      userInfo: {
+        id: 0,
+        uname: "",
+        phone: "",
+        password: null,
+        picture: null,
+        balance: 0,
+        userRole: "",
+        createTime: "",
+      },
+      dialog: false,
+      showAlert: false,
+      value: 0,
+      msg: "",
     };
   },
 
-  methods: {},
+  methods: {
+    async handleRecharge() {
+      recharge({ userId: this.userInfo.id, value: this.value }).then((res) => {
+        console.log(res);
+        if (res && res.code === 1) {
+          this.msg = res.msg;
+          this.dialog = false;
+          this.showAlert = true;
+          this.refreshUserInfo();
+          setTimeout(() => {
+            this.showAlert = false;
+          }, 1000);
+        }
+      });
+    },
+
+    async refreshUserInfo() {
+      const userId = window.localStorage.getItem("userId");
+      this.userInfo = await getUser(userId);
+    },
+  },
+
+  async mounted() {
+    await this.refreshUserInfo();
+  },
 };
 </script>
+
+<style scoped>
+.alert {
+  position: fixed;
+  left: 50%;
+  top: 100px;
+}
+</style>

+ 85 - 20
src/views/teacher/CourseCreate.vue

@@ -1,23 +1,47 @@
 <template>
   <div>
+    <!-- alert -->
+    <v-alert
+      class="dialog"
+      outlined
+      type="success"
+      text
+      v-show="showSuccessDialog"
+      transition="scroll-y-transition"
+    >
+      课程创建成功!
+    </v-alert>
+    <!-- alert -->
+    <v-alert
+      class="dialog"
+      outlined
+      type="warning"
+      text
+      v-show="showFailDialog"
+      transition="scroll-y-transition"
+    >
+      {{ msg }}
+    </v-alert>
     <v-container class="pl-16 pr-16">
       <form class="pa-12 grey lighten-5 mt-8">
-        <v-text-field v-model="name" label="课程名称"></v-text-field>
-
-        <v-text-field v-model="description" label="课程描述"></v-text-field>
-        <v-file-input
-          chips
-          label="上传课件"
-          v-model="instructionFile"
-        ></v-file-input>
-
+        <v-text-field v-model="courseInfo.name" label="课程名称"></v-text-field>
+        <v-select
+          :items="types"
+          v-model="courseInfo.type"
+          label="课程分类"
+        ></v-select>
+        <v-textarea v-model="courseInfo.intro" label="课程简介"></v-textarea>
+        <v-text-field
+          v-model="courseInfo.school"
+          label="所属学校"
+        ></v-text-field>
         <v-switch
-          v-model="switch1"
-          :label="`是否为付费课程: ${switch1.toString()}`"
+          v-model="isCharge"
+          :label="`是否为付费课程: ${isCharge.toString()}`"
         ></v-switch>
         <v-text-field
-          v-show="switch1"
-          v-model="cost"
+          v-show="isCharge"
+          v-model="courseInfo.cost"
           label="课程价格(元)"
         ></v-text-field>
         <v-btn class="ml-0 mt-8 info" @click="submit">
@@ -49,26 +73,67 @@
 </template>
 
 <script>
-// @ is an alias to /src
+import { createCourse } from "@/api/course";
 
 export default {
   name: "CourseCreate",
 
   data() {
     return {
-      name: "",
-      description: "",
+      courseInfo: {
+        name: "",
+        type: "初级",
+        intro: "",
+        picture: "course1.png",
+        school: "",
+        cost: 0,
+      },
+      isCharge: false,
+      types: ["初级", "中级", "高级"],
       dialog: false,
-      switch1: false,
-      cost: 0,
-      instructionFile: null,
+      showSuccessDialog: false,
+      showFailDialog: false,
+      msg: "",
     };
   },
 
   methods: {
     submit() {
-      console.log(this.name, this.description);
+      const uid = window.localStorage.getItem("userId");
+      const uname = window.localStorage.getItem("username");
+      const payload = {
+        ...this.courseInfo,
+        picture: null,
+        teacherId: uid,
+        teacherName: uname,
+        bought: false,
+        manageable: true,
+      };
+      console.log(payload);
+      createCourse(payload).then((res) => {
+        console.log(res);
+        if (res.code === 1) {
+          this.showSuccessDialog = true;
+          setTimeout(() => {
+            this.showSuccessDialog = false;
+          }, 1000);
+        } else {
+          this.showFailDialog = true;
+          this.msg = res.msg;
+          setTimeout(() => {
+            this.showFailDialog = false;
+          }, 1000);
+        }
+      });
     },
   },
 };
 </script>
+
+<style scoped>
+.dialog {
+  position: fixed;
+  left: 50%;
+  top: 100px;
+}
+</style>

+ 295 - 54
src/views/teacher/CourseEdit.vue

@@ -1,76 +1,317 @@
 <template>
   <div>
     <v-container class="pl-16 pr-16">
-      <form class="pa-12 grey lighten-5 mt-8">
-        <v-text-field v-model="name" label="课程名称" disabled></v-text-field>
-
-        <v-text-field
-          v-model="description"
-          label="课程描述"
-          disabled
-        ></v-text-field>
-        <v-file-input
-          chips
-          label="上传课件"
-          v-model="instructionFile"
-        ></v-file-input>
-
-        <v-switch
-          v-model="switch1"
-          :label="`是否为付费课程: ${switch1.toString()}`"
-        ></v-switch>
-        <v-text-field
-          v-show="switch1"
-          v-model="cost"
-          label="课程价格(元)"
-        ></v-text-field>
-        <v-btn class="ml-0 mt-8 info" @click="submit">
-          确认
-        </v-btn>
-      </form>
-    </v-container>
+      <!-- alert-->
+      <v-alert
+        class="dialog"
+        outlined
+        type="success"
+        text
+        v-show="showAlert"
+        transition="scroll-y-transition"
+      >
+        {{ alertMsg }}
+      </v-alert>
+
+      <!-- dialog-->
+      <template>
+        <v-row justify="center">
+          <v-dialog v-model="dialog" persistent max-width="600px">
+            <v-card>
+              <v-card-title>
+                <span class="headline">编辑课件</span>
+              </v-card-title>
+              <v-card-text>
+                <v-container>
+                  <v-row>
+                    <v-col cols="12" sm="6" md="4">
+                      <v-text-field
+                        label="文件类型"
+                        v-model="currentEditFile.fileType"
+                        disabled
+                      ></v-text-field>
+                    </v-col>
+                    <v-col cols="12" sm="6" md="4">
+                      <v-text-field
+                        label="文件大小"
+                        v-model="currentEditFile.fileSize"
+                        disabled
+                      ></v-text-field>
+                    </v-col>
+                    <v-col cols="12">
+                      <v-text-field
+                        label="文件名"
+                        v-model="currentEditFile.fileName"
+                        disabled
+                      ></v-text-field>
+                    </v-col>
+                    <v-col cols="12">
+                      <v-text-field
+                        label="课件标题"
+                        v-model="currentEditFile.title"
+                      ></v-text-field>
+                    </v-col>
+                    <v-col cols="12" sm="6">
+                      <v-select
+                        :items="[true, false]"
+                        label="是否免费"
+                        v-model="currentEditFile.freeFlag"
+                      ></v-select>
+                    </v-col>
+                    <v-col cols="12" sm="6">
+                      <v-select
+                        :items="[true, false]"
+                        label="是否可下载"
+                        v-model="currentEditFile.downloadFlag"
+                      ></v-select>
+                    </v-col>
+                    <v-col cols="12" sm="6">
+                      <v-select
+                        :items="[true, false]"
+                        label="是否有权查看"
+                        v-model="currentEditFile.availableFlag"
+                      ></v-select>
+                    </v-col>
+                  </v-row>
+                </v-container>
+              </v-card-text>
+              <v-card-actions>
+                <v-spacer></v-spacer>
+                <v-btn color="blue darken-1" text @click="dialog = false">
+                  Close
+                </v-btn>
+                <v-btn color="blue darken-1" text @click="handleEditSubmit">
+                  Save
+                </v-btn>
+              </v-card-actions>
+            </v-card>
+          </v-dialog>
+        </v-row>
+      </template>
+
+      <v-row>
+        <v-col>
+          <v-card max-width="600" class="mx-auto mt-8">
+            <v-toolbar color="primary" dark>
+              <v-app-bar-nav-icon></v-app-bar-nav-icon>
+              <v-toolbar-title>课程课件</v-toolbar-title>
+              <v-spacer></v-spacer>
+
+              <v-file-input
+                hide-input
+                prepend-icon="mdi-plus"
+                v-model="instructionFile"
+                @change="handleUploadFile"
+              ></v-file-input>
+            </v-toolbar>
+
+            <v-list subheader two-line>
+              <v-list-item v-for="file in files" :key="file.title">
+                <v-list-item-avatar>
+                  <v-icon class="grey lighten-1" dark>
+                    mdi-file
+                  </v-icon>
+                </v-list-item-avatar>
+
+                <v-list-item-content>
+                  <v-list-item-title v-text="file.title"></v-list-item-title>
+
+                  <v-list-item-subtitle
+                    v-text="file.fileSize"
+                  ></v-list-item-subtitle>
+                </v-list-item-content>
 
-    <!-- 提示对话框 -->
-    <v-dialog v-model="dialog" width="500">
-      <v-card>
-        <v-card-title> </v-card-title>
-
-        <v-card-text>
-          专题修改成功。
-        </v-card-text>
-
-        <v-divider></v-divider>
-
-        <v-card-actions>
-          <v-spacer></v-spacer>
-          <v-btn color="primary" text @click="dialog = false">
-            确认
-          </v-btn>
-        </v-card-actions>
-      </v-card>
-    </v-dialog>
+                <v-list-item-action>
+                  <v-btn icon>
+                    <v-icon
+                      color="grey lighten-1"
+                      @click="showEditFileDialog(file.id)"
+                      >mdi-more</v-icon
+                    >
+                  </v-btn>
+                </v-list-item-action>
+              </v-list-item>
+            </v-list>
+          </v-card>
+        </v-col>
+        <v-col>
+          <form class="pa-12 grey lighten-5 mt-8">
+            <v-text-field
+              v-model="courseInfo.name"
+              label="课程名称"
+              readonly
+            ></v-text-field>
+            <v-text-field
+              v-model="courseInfo.id"
+              label="课程ID"
+              readonly
+            ></v-text-field>
+            <v-select
+              :items="types"
+              v-model="courseInfo.type"
+              label="课程分类"
+              readonly
+            ></v-select>
+            <v-textarea
+              v-model="courseInfo.intro"
+              label="课程简介"
+              readonly
+            ></v-textarea>
+            <v-text-field
+              v-model="courseInfo.school"
+              label="所属学校"
+              readonly
+            ></v-text-field>
+            <v-text-field
+              v-model="courseInfo.createTime"
+              label="创建时间"
+              readonly
+            ></v-text-field>
+            <v-text-field
+              v-model="courseInfo.teacherName"
+              label="创建教师"
+              readonly
+            ></v-text-field>
+            <v-switch
+              v-model="isCharge"
+              :label="`是否为付费课程: ${isCharge.toString()}`"
+              readonly
+            ></v-switch>
+            <v-text-field
+              v-show="isCharge"
+              v-model="courseInfo.cost"
+              label="课程价格(元)"
+              readonly
+            ></v-text-field>
+          </form>
+        </v-col>
+      </v-row>
+    </v-container>
   </div>
 </template>
 
 <script>
+import { uploadFile } from "@/api/file";
+import {
+  getCourseWareByCourse,
+  createCourseWare,
+  updateCourseWare,
+} from "@/api/courseWare";
+import { getCourseById } from "@/api/course";
+
 export default {
   name: "CourseEdit",
 
   data() {
     return {
-      name: "",
-      description: "",
-      dialog: false,
-      switch1: false,
-      cost: 0,
+      courseInfo: {},
       instructionFile: null,
+      types: ["初级", "中级", "高级"],
+      msg: "",
+      files: [],
+      showAlert: false,
+      alertMsg: "",
+      dialog: false,
+      currentEditFile: {
+        id: 0,
+        courseId: 0,
+        number: 0,
+        title: "",
+        fileName: "",
+        fileType: "",
+        fileSize: "",
+        freeFlag: false,
+        downloadFlag: true,
+        availableFlag: false,
+        uploadTime: "",
+      },
     };
   },
 
+  computed: {
+    isCharge: function() {
+      return this.courseInfo.cost !== 0;
+    },
+  },
+
   methods: {
-    submit() {
-      console.log(this.name, this.description);
+    async handleUploadFile() {
+      const formData = new window.FormData();
+      formData.append("file", this.instructionFile);
+      const res = await uploadFile(formData);
+      if (res.code === 1) {
+        const { courseId } = this.$route.params;
+        const fileInfo = {
+          courseId: courseId,
+          number: this.files.length,
+          title: res.data.name,
+          fileName: res.data.name,
+          fileType: res.data.type,
+          fileSize: res.data.size,
+          freeFlag: true,
+          downloadFlag: true,
+          availableFlag: true,
+        };
+        createCourseWare(fileInfo).then((res) => {
+          console.log(res);
+          this.alertMsg = res.msg;
+          this.showAlert = true;
+          setTimeout(() => {
+            this.showAlert = false;
+            this.refreshFile();
+          }, 1000);
+        });
+      }
+    },
+
+    refreshFile() {
+      const { courseId } = this.$route.params;
+      const uid = window.localStorage.getItem("userId");
+      getCourseWareByCourse({ courseId, uid }).then((res) => {
+        this.files = res || [];
+      });
+    },
+
+    loadCourse() {
+      const { courseId } = this.$route.params;
+      const uid = window.localStorage.getItem("userId");
+      getCourseById({ uid, courseId }).then((res) => {
+        this.courseInfo = res;
+      });
+    },
+
+    showEditFileDialog(courseWareId) {
+      const currentFile = this.files.find((f) => f.id === courseWareId);
+      console.log(currentFile);
+      this.currentEditFile = currentFile;
+      this.dialog = true;
+    },
+
+    handleEditSubmit() {
+      updateCourseWare(this.currentEditFile).then((res) => {
+        this.dialog = false;
+        console.log(res);
+        this.alertMsg = res.msg;
+        this.showAlert = true;
+        setTimeout(() => {
+          this.showAlert = false;
+          this.refreshFile();
+        }, 1000);
+      });
     },
   },
+
+  mounted() {
+    this.refreshFile();
+    this.loadCourse();
+  },
 };
 </script>
+
+<style scoped>
+.dialog {
+  position: fixed;
+  left: 50%;
+  top: 100px;
+}
+</style>

+ 23 - 45
src/views/teacher/CourseList.vue

@@ -12,21 +12,26 @@
       </v-row>
       <v-row>
         <course-item-teacher
-          v-for="(item, index) in courseList"
-          :key="index"
-          :courseName="item.name"
-          :description="item.description"
-          :status="item.status"
-          :course-color="colorList[index % colorList.length]"
-        ></course-item-teacher>
+          v-for="course in courseList"
+          :key="course.id"
+          :courseName="course.name"
+          :courseId="course.id"
+          :description="course.intro"
+          :type="course.type"
+          :cost="course.cost"
+          :bought="course.bought"
+          :manageable="course.manageable"
+          :course-color="colorList[course.id % colorList.length]"
+        >
+        </course-item-teacher>
       </v-row>
     </v-container>
   </div>
 </template>
 
 <script>
-// @ is an alias to /src
 import CourseItemTeacher from "@/components/CourseItemTeacher.vue";
+import { getTeacherCourses } from "@/api/course";
 
 export default {
   name: "CourseList",
@@ -37,44 +42,17 @@ export default {
     return {
       colorList: ["#26A69A", "#00B0FF", "#5C6BC0", "#FFB300", "#E57373"],
 
-      courseList: [
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-        {
-          name: "软件工程",
-          description: "介绍软件工程的方法以及实践",
-          status: -1,
-        },
-      ],
+      courseList: null,
     };
   },
+  mounted() {
+    const uid = window.localStorage.getItem("userId");
+    if (uid) {
+      getTeacherCourses(uid).then((res) => {
+        console.log(res);
+        this.courseList = res || [];
+      });
+    }
+  },
 };
 </script>

+ 116 - 6
src/views/teacher/UserCenter.vue

@@ -1,26 +1,136 @@
 <template>
   <div>
+    <!-- alert -->
+    <v-alert
+      class="alert"
+      outlined
+      type="success"
+      text
+      v-show="showAlert"
+      transition="scroll-y-transition"
+    >
+      {{ msg }}
+    </v-alert>
+    <template>
+      <v-dialog v-model="dialog" max-width="400">
+        <v-card>
+          <v-card-title class="headline">
+            账户充值
+          </v-card-title>
+          <v-text-field
+            v-model="value"
+            label="充值金额"
+            class="pa-6"
+          ></v-text-field>
+
+          <v-card-actions>
+            <v-spacer></v-spacer>
+
+            <v-btn color="green darken-1" text @click="handleRecharge">
+              充值
+            </v-btn>
+
+            <v-btn color="green darken-1" text @click="dialog = false">
+              取消
+            </v-btn>
+          </v-card-actions>
+        </v-card>
+      </v-dialog>
+    </template>
+
     <v-container class="pl-16 pr-16">
       <form class="pa-12 grey lighten-5 mt-8">
-        <v-text-field v-model="username" label="用户名" disabled></v-text-field>
-
-        <v-text-field v-model="userId" label="ID" disabled></v-text-field>
+        <v-text-field v-model="userInfo.id" label="ID" readonly></v-text-field>
+        <v-text-field
+          v-model="userInfo.uname"
+          label="用户名"
+          readonly
+        ></v-text-field>
+        <v-text-field
+          v-model="userInfo.phone"
+          label="手机号"
+          readonly
+        ></v-text-field>
+        <v-text-field
+          v-model="userInfo.userRole"
+          label="用户角色"
+          readonly
+        ></v-text-field>
+        <v-text-field
+          v-model="userInfo.createTime"
+          label="注册时间"
+          readonly
+        ></v-text-field>
+        <v-text-field
+          v-model="userInfo.balance"
+          label="账户余额"
+          readonly
+        ></v-text-field>
+        <v-btn color="primary" dark @click.stop="dialog = true">
+          充值
+        </v-btn>
       </form>
     </v-container>
   </div>
 </template>
 
 <script>
+import { getUser, recharge } from "@/api/user";
+
 export default {
   name: "TeacherCenter",
 
   data() {
     return {
-      username: "kate",
-      account: 1111,
+      userInfo: {
+        id: 0,
+        uname: "",
+        phone: "",
+        password: null,
+        picture: null,
+        balance: 0,
+        userRole: "",
+        createTime: "",
+      },
+      dialog: false,
+      showAlert: false,
+      value: 0,
+      msg: "",
     };
   },
 
-  methods: {},
+  methods: {
+    async handleRecharge() {
+      recharge({ userId: this.userInfo.id, value: this.value }).then((res) => {
+        console.log(res);
+        if (res && res.code === 1) {
+          this.msg = res.msg;
+          this.dialog = false;
+          this.showAlert = true;
+          this.refreshUserInfo();
+          setTimeout(() => {
+            this.showAlert = false;
+          }, 1000);
+        }
+      });
+    },
+
+    async refreshUserInfo() {
+      const userId = window.localStorage.getItem("userId");
+      this.userInfo = await getUser(userId);
+    },
+  },
+
+  async mounted() {
+    await this.refreshUserInfo();
+  },
 };
 </script>
+
+<style scoped>
+.alert {
+  position: fixed;
+  left: 50%;
+  top: 100px;
+}
+</style>