浏览代码

新课标安排

lrf402788946 5 年之前
父节点
当前提交
3bb010defc

+ 6 - 0
src/router/index.js

@@ -36,6 +36,12 @@ const newPlan = [
     meta: { title: '班级安排' },
     component: () => import('@/views/new-plan/class/classes.vue'),
   },
+  {
+    path: '/newPlan/classes/lesson',
+    name: 'newPlan_classes_lesson',
+    meta: { title: '排课管理' },
+    component: () => import('@/views/new-plan/class/lesson.vue'),
+  },
 ];
 
 const routes = [

+ 2 - 0
src/store/index.js

@@ -19,6 +19,7 @@ import teaPlan from '@frame/store/tea-plan';
 import lesson from '@frame/store/lesson';
 import trainTemplate from '@frame/store/train-template';
 import leave from '@frame/store/leave';
+import util from '@frame/store/util';
 
 import nation from '@frame/store/nation';
 import completion from '@frame/store/question-completion';
@@ -58,6 +59,7 @@ export default new Vuex.Store({
     leave,
     trainTemplate,
     other,
+    util,
   },
   state: { ...ustate },
   mutations: { ...umutations },

+ 5 - 0
src/views/lesson/detail.vue

@@ -6,6 +6,10 @@
           <template v-if="item.model === 'type'">
             <el-radio v-for="(i, index) in types" :key="index" :label="i.value">{{ i.label }}</el-radio>
           </template>
+          <template v-if="item.model === 'allday'">
+            <el-radio label="0">全天</el-radio>
+            <el-radio label="1">半天</el-radio>
+          </template>
         </template>
         <template #custom="{item}">
           <template v-if="item.model === 'lesson'">
@@ -98,6 +102,7 @@ export default {
       fields: [
         { label: '模板名称', required: true, model: 'title' },
         { label: '类型', required: true, model: 'type', type: 'radio' },
+        { label: '最后一天时长', required: true, model: 'allday', type: 'radio' },
         { label: '课程安排', model: 'lesson', custom: true },
       ],
       types: [

+ 139 - 0
src/views/new-plan/class/lesson.vue

@@ -0,0 +1,139 @@
+<template>
+  <div id="index">
+    <list-frame v-if="view === 'list'" returns="./index" :title="pageTitle" :needFilter="false" :needAdd="false" :needPag="false">
+      <el-form :inline="true">
+        <el-form-item>
+          <el-select v-model="prerequisite.term" size="small" @change="toGetClass">
+            <el-option-group v-for="(term, index) in termList" :key="index" :label="`第${term.term}期`">
+              <el-option
+                v-for="(batch, bindex) in term.batchnum"
+                :key="`${index}-${bindex}`"
+                :label="batch.type == '0' ? `第${batch.batch}批` : `特殊批`"
+                :value="batch._id"
+              ></el-option>
+            </el-option-group>
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <el-row type="flex" align="middle" justify="end" style="padding:10px">
+        <el-col :span="2">
+          <el-button type="primary" size="mini" plain @click="toArrange">按模板排课</el-button>
+        </el-col>
+      </el-row>
+      <data-table ref="table" :fields="fields" :data="list" :opera="opera" @date="toDate" @msg="toMsg" @bedroom="toBedroom"></data-table>
+    </list-frame>
+    <detail-frame v-else title="返回" :returns="deReturn">
+      <lesson-table :classInfo="classInfo"></lesson-table>
+    </detail-frame>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash';
+import listFrame from '@frame/layout/admin/list-frame';
+import detailFrame from '@frame/layout/admin/detail-frame';
+import dataTable from '@frame/components/data-table';
+import lessonTable from './lesson/lesson-table.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: trainPlan } = createNamespacedHelpers('trainplan');
+const { mapActions: classes } = createNamespacedHelpers('classes');
+const { mapActions: lesson } = createNamespacedHelpers('lesson');
+export default {
+  name: 'index',
+  props: {},
+  components: { listFrame, detailFrame, dataTable, lessonTable },
+  data: () => {
+    return {
+      view: 'list',
+      termList: [],
+      batchList: [],
+      list: [],
+      prerequisite: {},
+      classInfo: {},
+      opera: [
+        {
+          label: '排课',
+          icon: 'el-icon-date',
+          method: 'date',
+        },
+        {
+          label: '一键分寝',
+          icon: 'el-icon-s-home',
+          method: 'bedroom',
+        },
+        {
+          label: '发送确认通知',
+          icon: 'el-icon-message-solid',
+          method: 'msg',
+        },
+      ],
+      fields: [
+        { label: '班级', prop: 'name' },
+        // { label: '批数', prop: 'batch' },
+      ],
+    };
+  },
+  created() {
+    this.search();
+  },
+  methods: {
+    ...trainPlan({ getTrainPlan: 'fetch', sendNotice: 'notice' }),
+    ...classes({ getClass: 'query', updateClass: 'update' }),
+    ...lesson({ autoArrange: 'arrange' }),
+    async search({ skip = 0, limit = 10, ...info } = {}) {
+      let res = await this.getTrainPlan(this.id);
+      if (this.$checkRes(res)) {
+        this.$set(this, `termList`, _.get(res.data, 'termnum', []));
+      }
+    },
+    async toDate({ data }) {
+      this.view = 'class';
+      this.$set(this, `classInfo`, JSON.parse(JSON.stringify(data)));
+    },
+    async toMsg({ data }) {
+      console.log(`in toMsg`);
+    },
+    async toBedroom({ data }) {
+      console.log(`in toBedroom`);
+    },
+    async toGetClass(data) {
+      let res = await this.getClass({ batchid: data });
+      if (this.$checkRes(res)) {
+        this.$set(this, `list`, res.data);
+      }
+    },
+    deReturn() {
+      this.view = 'list';
+    },
+    async toArrange() {
+      this.$confirm('此操作将会把本期下所有的班级课表重置,若您已经修改过某班的信息,请谨慎使用', '提示', {
+        confirmButtonText: '按模板排课',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(async () => {
+          console.log('in function:');
+          let res = await this.autoArrange(this.id);
+          this.$checkRes(res, '排课成功', res.errmsg || '排课失败');
+        })
+        .catch(async () => {
+          console.log('已取消');
+        });
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+    id() {
+      return this.$route.query.id;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 338 - 0
src/views/new-plan/class/lesson/lesson-table.vue

@@ -0,0 +1,338 @@
+<template>
+  <div id="lesson-table">
+    <el-card>
+      <template #header>
+        <el-row type="flex" align="middle" justify="space-between">
+          <el-col :span="4">课程安排</el-col>
+          <el-col :span="2">
+            <el-button type="primary" size="mini" @click="toSave">保存课表</el-button>
+          </el-col>
+        </el-row>
+      </template>
+      <el-table :data="lessonList" border stripe @cell-click="cellClick">
+        <el-table-column align="center" label="时间" prop="time"></el-table-column>
+        <el-table-column align="center" v-for="(i, index) in dateList" :key="index" :label="i" :prop="`subname_day${index + 1}`"></el-table-column>
+      </el-table>
+    </el-card>
+
+    <el-drawer :visible.sync="drawer" direction="rtl" title="课程安排" @close="toClose">
+      <data-form :data="form" :fields="fields" :rules="{}" @save="handleSave" :reset="false">
+        <template #radios="{item, form}">
+          <template v-if="item.model == 'type'">
+            <el-radio @change="radioClearForm" v-for="(i, index) in dayType" :key="index" :label="i.label">{{ i.label }}</el-radio>
+          </template>
+        </template>
+        <template #options="{item, form}">
+          <template v-if="item.model == 'subname'">
+            <el-option v-for="(i, index) in actList" :key="index" :label="i.label" :value="i.label"></el-option>
+          </template>
+          <template v-if="item.model == 'subid'">
+            <el-option v-for="(i, index) in subjectList" :key="index" :label="i.name" :value="i.id"></el-option>
+          </template>
+        </template>
+
+        <template #custom="{item, form}">
+          <template v-if="item.model == 'teaname'">
+            <el-input v-model="form.teaname" :readonly="true" placeholder="点击选择教师" @click.native="toChooseTeacher"></el-input>
+          </template>
+        </template>
+      </data-form>
+    </el-drawer>
+
+    <el-dialog title="选择教师" :visible.sync="dialog" :destroy-on-close="true">
+      <teacher-select :schoolList="schoolList" :subjectList="subjectList" :subjectid="form.subid" @selTea="selTea"> </teacher-select>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+var moment = require('moment');
+import _ from 'lodash';
+import dataForm from '@frame/components/form';
+import teacherSelect from './teacher.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: lesson } = createNamespacedHelpers('lesson');
+const { mapActions: mapUtil } = createNamespacedHelpers('util');
+const { mapActions: subject } = createNamespacedHelpers('subject');
+const { mapActions: teacher } = createNamespacedHelpers('teacher');
+const { mapActions: school } = createNamespacedHelpers('school'); //给选老师组件使用.这个页面请求完就不销毁了
+
+// 本页的组合数据,变量用x表示: _id_day[x];subname_day[x];subid_day[x];teaid_day[x];teaname_day:[x]
+export default {
+  name: 'lesson-table',
+  props: {
+    classInfo: { type: Object, default: () => {} },
+  },
+  components: { dataForm, teacherSelect },
+  data: function() {
+    var that = this;
+    return {
+      lessonInfo: {},
+      lessonList: [],
+      dateList: [],
+      timeList: [],
+      subjectList: [],
+      teacherList: [],
+      schoolList: [], //给选老师组件用
+      drawer: false,
+      dialog: false,
+      form: {},
+      fields: [
+        { label: '日期', model: 'date', type: 'text' },
+        { label: '时间', model: 'time', type: 'text' },
+        { label: '类型', model: 'type', type: 'radio' },
+        { label: '课程安排', model: 'subid', type: 'select', display: (fields, form) => that.fieldDisplay(fields, form) },
+        { label: '活动安排', model: 'subname', type: 'select', display: (fields, form) => that.fieldDisplay(fields, form) },
+        { label: '教师', model: 'teaname', custom: true, display: (fields, form) => that.fieldDisplay(fields, form) },
+      ],
+      dayType: [{ label: '活动' }, { label: '课程' }],
+      actList: [
+        { label: '--' },
+        { label: '报道+开班仪式' },
+        { label: '午餐+休息' },
+        { label: '晚餐' },
+        { label: '团队组建' },
+        { label: '拓展交流' },
+        { label: '课程作业小组展示' },
+        { label: '课程作业' },
+        { label: '礼仪课小组面试' },
+        { label: '结业仪式' },
+      ],
+    };
+  },
+  created() {
+    this.getOtherList();
+  },
+  methods: {
+    ...mapUtil(['fetch']),
+    ...lesson(['query', 'create', 'update']),
+    ...subject({ getSubject: 'query' }),
+    ...teacher({ getTeacher: 'query' }),
+    ...school({ getSchool: 'query' }),
+    async search() {
+      let res = await this.fetch({ model: 'lesson', classid: _.get(this.classInfo, '_id') });
+      if (this.$checkRes(res)) {
+        if (res.data.lessons.length <= 0) this.$message.warning('请先将本期的课程按模板进行初始化');
+        this.$set(this, `lessonInfo`, _.omit(res.data, ['lessons']));
+        let arr = _.get(res.data, `lessons`, []);
+        let x = this.getX(JSON.parse(JSON.stringify(arr)));
+        this.getY(JSON.parse(JSON.stringify(arr)));
+        this.$set(this, `dateList`, x);
+        arr = this.aData(arr);
+        this.$set(this, `lessonList`, arr);
+      }
+    },
+    async toSave() {
+      //整理成原数据形式,提交
+      let data = JSON.parse(JSON.stringify(this.lessonList));
+      data = this.returnData(data);
+      let lesson = JSON.parse(JSON.stringify(this.lessonInfo));
+      lesson.lessons = data;
+      let res = await this.update(lesson);
+      if (this.$checkRes(res, '课程表保存成功', res.errmsg || '课程表保存失败')) {
+        console.log(`in`);
+      }
+    },
+    //点击单元格事件
+    cellClick(row, column) {
+      let date = _.get(column, 'label');
+      let time = _.get(row, 'time');
+      let num = _.get(column, 'property').match(/\d+(.\d+)?/g)[0];
+      let obj = this.getOrderDate(row, num);
+      obj.type = obj.subid ? '课程' : '活动';
+      this.$set(this, `form`, { date, time, ...obj });
+      this.drawer = true;
+    },
+    //抽屉保存
+    handleSave({ data }) {
+      let num = _.get(data, 'index');
+      let type = _.get(data, `type`);
+      let time = _.get(data, `time`);
+      let yIndex = this.lessonList.findIndex(f => f.time == time);
+      let obj = {};
+      if (type == '课程') {
+        obj = _.pick(data, ['subid', 'teaid', 'teaname', '_id']);
+        let r = this.subjectList.find(f => f.id == obj.subid);
+        if (r) obj.subname = r.name;
+      } else {
+        obj = _.pick(data, ['subname', '_id']);
+      }
+      obj = this.resetData(obj, num);
+      this.$set(this.lessonList, yIndex, { ...this.lessonList[yIndex], ...obj });
+      this.drawer = false;
+    },
+    //提交整理数据
+    returnData(data) {
+      let returnArr = [];
+      data.map(i => {
+        let keys = Object.keys(i);
+        let time = _.get(i, `time`);
+        let arr = _.compact(_.uniq(_.flatten(keys.map(i => i.match(/\d+(.\d+)?/g)))));
+        arr.map(index => {
+          let obj = this.getOrderDate(i, index, true);
+          obj.time = time;
+          obj.day = '0';
+          returnArr.push(obj);
+        });
+      });
+      let r = returnArr.filter(f => f.date == _.last(this.dateList));
+      let allday = '0';
+      let res = r.find(f => {
+        //TODO 根据开始时间不超过12点判断是 整天还是半天
+        if (f.subname != '--') {
+          let ts = f.time.split('-');
+          let time = moment(`${f.date} ${ts[0]}`).format('X');
+          let twl = moment(`${f.date} 12:00`).format('X');
+          return twl <= time;
+        }
+      });
+      if (res) allday = '1';
+      returnArr = returnArr.map(i => {
+        if (i.date == _.last(this.dateList)) i.allday = allday;
+        else i.allday = '0';
+        return i;
+      });
+      return returnArr;
+    },
+    //field的显示
+    fieldDisplay(f, form) {
+      if (f.model == 'teaname' || f.model == 'subid') {
+        return form.type == '课程';
+      } else return form.type == '活动';
+    },
+    //请求后整理数据方法
+    aData(data) {
+      let duplicate = JSON.parse(JSON.stringify(data));
+      //按时间分组
+      duplicate = _.flatten(_.toPairs(_.groupBy(data, 'time'))).filter(f => _.isArray(f));
+      let r = duplicate.map(i => {
+        //按日期排序
+        let aa = i.sort((a, b) => moment(a.date).format('X') - moment(b.date).format('X'));
+        //组合数据:{time,day1,id_day1,subid_day1}
+        let object = { time: _.get(i[0], 'time') };
+        aa.map(a => {
+          let index = this.dateList.findIndex(f => f == a.date);
+          if (index >= 0) {
+            index = index + 1;
+          }
+          let obj = this.resetData(a, index);
+          object = { ...object, ...obj };
+        });
+        return object;
+      });
+      r = this.getOrderForTime(r);
+      return r;
+    },
+    //获取指定数据
+    getOrderDate(data, index, needDate = false) {
+      let obj = { index: index };
+      if (_.get(data, `_id_day${index}`)) obj[`_id`] = _.get(data, `_id_day${index}`);
+      obj[`subname`] = _.get(data, `subname_day${index}`, `--`);
+      if (_.get(data, `subid_day${index}`)) obj[`subid`] = _.get(data, `subid_day${index}`);
+      if (_.get(data, `teaid_day${index}`)) obj[`teaid`] = _.get(data, `teaid_day${index}`);
+      if (_.get(data, `teaname_day${index}`)) obj[`teaname`] = _.get(data, `teaname_day${index}`);
+      if (needDate) {
+        //所有的数据都还原了,没必要遥index了
+        delete obj.index;
+        obj.date = this.dateList[index - 1];
+      }
+      return obj;
+    },
+    //整理,匹配数据是哪天,该显示在哪
+    resetData(data, index) {
+      let obj = {};
+      if (_.get(data, '_id')) obj[`_id_day${index}`] = _.get(data, '_id');
+      obj[`subname_day${index}`] = _.get(data, 'subname', '--');
+      if (_.get(data, 'subid')) obj[`subid_day${index}`] = _.get(data, 'subid');
+      if (_.get(data, 'teaid')) obj[`teaid_day${index}`] = _.get(data, 'teaid');
+      if (_.get(data, 'teaname')) obj[`teaname_day${index}`] = _.get(data, 'teaname');
+      return obj;
+    },
+    //根据时间排序
+    getOrderForTime(data) {
+      let duplicate = JSON.parse(JSON.stringify(data));
+      duplicate = duplicate.sort((a, b) => {
+        let a_arr = a.time.split('-');
+        let b_arr = b.time.split('-');
+        let at = moment(`${moment().format('YYYY-MM-DD')} ${a_arr[0]}`).format('X');
+        let bt = moment(`${moment().format('YYYY-MM-DD')} ${b_arr[0]}`).format('X');
+        return at - bt;
+      });
+      return duplicate;
+    },
+    //整理出标头,根据日期排序
+    getX(data) {
+      let r = _.uniqBy(data, 'date').map(i => i.date);
+      r = r.sort((a, b) => moment(a).format('X') - moment(b).format('X'));
+      return r;
+    },
+    //获得时间列表
+    getY(data) {
+      let duplicate = JSON.parse(JSON.stringify(data));
+      let arr = _.uniqBy(
+        duplicate.map(i => _.pick(i, ['time'])),
+        'time'
+      );
+      arr = this.getOrderForTime(arr);
+      this.$set(
+        this,
+        `timeList`,
+        arr.map(i => i.time)
+      );
+    },
+    //教师列表,课程列表
+    async getOtherList() {
+      let res = await this.getSubject();
+      if (this.$checkRes(res)) this.$set(this, `subjectList`, res.data);
+      res = await this.getTeacher({ status: '4' });
+      if (this.$checkRes(res)) this.$set(this, `teacherList`, res.data);
+    },
+    //关闭抽屉
+    toClose() {
+      this.drawer = false;
+      this.form = {};
+    },
+    //修改类型清除数据
+    radioClearForm(data) {
+      if (data == '活动') {
+        delete this.form.subid;
+        this.form.subname = '--';
+      }
+    },
+    //打开选择教师的dialog
+    async toChooseTeacher() {
+      this.dialog = true;
+      if (this.schoolList.length <= 0) {
+        let res = await this.getSchool();
+        if (this.$checkRes(res)) this.$set(this, `schoolList`, res.data);
+      }
+    },
+    //选择教师
+    selTea(data) {
+      this.dialog = false;
+      this.$set(this, `form`, { ...this.form, ...data });
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  watch: {
+    classInfo: {
+      handler(val) {
+        let id = _.get(val, '_id');
+        if (id) this.search();
+      },
+      immediate: true,
+      deep: true,
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 97 - 0
src/views/new-plan/class/lesson/teacher.vue

@@ -0,0 +1,97 @@
+<template>
+  <div id="teacher">
+    <el-tabs v-model="teaTab">
+      <el-tab-pane style="padding:10px" label="申请授课教师" name="apply">
+        <filter-table :data="applyList" :fields="teaFields" :opera="opera" @select="selectTeacher" :total="applyTotal" @query="toGetApplyList"></filter-table>
+      </el-tab-pane>
+      <el-tab-pane style="padding:10px" label="可授课教师" name="list">
+        <filter-table
+          :data="teacherList"
+          :fields="teaFields"
+          :opera="opera"
+          @select="selectTeacher"
+          :total="teacherTotal"
+          @query="toGetTeacherList"
+        ></filter-table>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import filterTable from '@frame/components/filter-page-table.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: teacher } = createNamespacedHelpers('teacher'); //教师
+const { mapActions: teaPlan } = createNamespacedHelpers('teaPlan'); //教师申请
+export default {
+  name: 'teacher',
+  props: {
+    schoolList: { type: Array, default: () => [] },
+    subjectList: { type: Array, default: () => [] },
+    subjectid: { type: String, default: () => '' },
+  },
+  components: { filterTable },
+  data: function() {
+    return {
+      teaTab: 'apply',
+      teaFields: [
+        { label: '姓名', prop: 'name', filter: 'input' },
+        { label: '学校', prop: 'schname' },
+        { label: '资料评分', prop: 'zlscore' },
+        { label: '面试评分', prop: 'msscore' },
+      ],
+      opera: [
+        {
+          label: '选择教师',
+          icon: 'el-icon-check',
+          method: 'select',
+        },
+      ],
+      applyList: [],
+      applyTotal: 0,
+      teacherList: [],
+      teacherTotal: 0,
+    };
+  },
+  created() {},
+  methods: {
+    ...teacher({ getTeacherList: 'query' }),
+    ...teaPlan({ getApplyTeacherList: 'applyQuery' }),
+    async toGetTeacherList({ skip = 0, limit = 10, ...info } = {}) {
+      let res = await this.getTeacherList({ skip, limit, ...info, subid: this.subjectid, status: '4' });
+      this.$set(this, `teacherTotal`, res.total);
+      this.$set(this, `teacherList`, res.data);
+    },
+    async toGetApplyList({ skip = 0, limit = 10, ...info } = {}) {
+      let res = await this.getApplyTeacherList({ skip, limit, ...info, subid: this.subjectid, status: '4' });
+      this.$set(this, `applyTotal`, res.total);
+      this.$set(this, `applyList`, res.data);
+    },
+    selectTeacher({ data }) {
+      this.$emit('selTea', { teaname: data.name, teaid: data._id });
+    },
+  },
+  watch: {
+    subjectid: {
+      handler(val, oval) {
+        if (val && val != oval) {
+          this.toGetApplyList();
+          this.toGetTeacherList();
+        }
+      },
+      immediate: true,
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>