lrf402788946 5 سال پیش
والد
کامیت
ffe63371c7

+ 2 - 0
package.json

@@ -11,6 +11,8 @@
     "@fullcalendar/core": "^4.3.1",
     "@fullcalendar/daygrid": "^4.3.0",
     "@fullcalendar/interaction": "^4.3.0",
+    "@fullcalendar/list": "^4.4.2",
+    "@fullcalendar/timegrid": "^4.4.2",
     "@fullcalendar/vue": "^4.3.1",
     "@stomp/stompjs": "^5.4.3",
     "axios": "^0.19.1",

+ 1 - 1
src/main.js

@@ -7,7 +7,7 @@ import '@/plugins/axios';
 import '@/plugins/check-res';
 import '@/plugins/element';
 import '@/plugins/setting';
-import '@frame/config/other';
+import '@/plugins/other';
 import '@/plugins/moment';
 import '@/plugins/dateM';
 import InitStomp from '@/plugins/stomp';

+ 2 - 0
src/plugins/other.js

@@ -0,0 +1,2 @@
+import Vue from 'vue';
+Vue.config.jhAppKey = 'ed73fa73956ff995bad705d664002595';

+ 21 - 2
src/router/index.js

@@ -211,8 +211,26 @@ const newPlan = [
   {
     path: '/newPlan/arrange',
     name: 'newPlan_arrange',
-    meta: { title: '计划安排' },
-    component: () => import('@/views/new-plan/arrange.vue'),
+    meta: { title: '培训计划安排' },
+    component: () => import('@/views/new-plan/arrange/arrange.vue'),
+  },
+  {
+    path: '/newPlan/director',
+    name: 'newPlan_director',
+    meta: { title: '班主任计划安排' },
+    component: () => import('@/views/new-plan/arrange/director-arrange.vue'),
+  },
+  {
+    path: '/newPlan/school/num',
+    name: 'newPlan_school_num',
+    meta: { title: '学校人数安排' },
+    component: () => import('@/views/new-plan/arrange/school-num.vue'),
+  },
+  {
+    path: '/newPlan/school',
+    name: 'newPlan_school',
+    meta: { title: '学校计划安排' },
+    component: () => import('@/views/new-plan/arrange/school-arrange.vue'),
   },
   {
     path: '/newPlan/classes/index',
@@ -406,6 +424,7 @@ const router = new VueRouter({
 });
 router.beforeEach((to, form, next) => {
   store.commit('setUser');
+  store.dispatch('setting/fetch');
   if (to.name === 'login') {
     next();
     return;

+ 9 - 2
src/store/index.js

@@ -21,6 +21,8 @@ import trainTemplate from '@frame/store/train-template';
 import leave from '@frame/store/leave';
 import util from '@frame/store/util';
 import count from '@frame/store/count';
+import setting from '@frame/store/setting';
+import trainBatch from '@frame/store/train-plan-year';
 
 import nation from '@frame/store/nation';
 import completion from '@frame/store/question-completion';
@@ -29,6 +31,9 @@ import dirPlan from '@frame/store/dir-plan';
 import login from '@frame/store/login';
 import * as ustate from '@frame/store/user/state';
 import * as umutations from '@frame/store/user/mutations';
+import * as dostate from '@frame/store/setting/state';
+import * as domutations from '@frame/store/setting/mutations';
+
 import other from '@frame/store/other';
 
 Vue.use(Vuex);
@@ -62,8 +67,10 @@ export default new Vuex.Store({
     other,
     util,
     count,
+    setting,
+    trainBatch,
   },
-  state: { ...ustate },
-  mutations: { ...umutations },
+  state: { ...ustate, ...dostate },
+  mutations: { ...umutations, ...domutations },
   actions: {},
 });

+ 79 - 53
src/views/new-plan/arrange.vue

@@ -1,13 +1,8 @@
 <template>
   <div id="arrange">
-    <detail-frame :title="pageTitle" :returns="returns">
-      <el-steps :active="view == 'plan' ? 1 : view == 'school' ? 2 : 3" align-center style="padding-bottom:10px">
-        <el-step title="安排培训计划"></el-step>
-        <el-step title="安排学校计划"></el-step>
-        <el-step title="安排班主任计划"></el-step>
-      </el-steps>
-      <el-row :gutter="10" type="flex" v-if="view == 'plan'">
-        <el-col :span="12">
+    <!-- <detail-frame :title="pageTitle" :returns="returns"> -->
+    <el-row type="flex" justify="center" v-if="view == 'plan'">
+      <!-- <el-col :span="12">
           <el-card header="全年计划信息">
             <el-form :model="info" :rules="rules" :isNew="isNew" label-width="60px" size="small" @submit.native.prevent>
               <el-form-item label="年份" required>
@@ -31,20 +26,27 @@
               </el-form-item>
             </el-form>
           </el-card>
-        </el-col>
-        <el-col :span="16" :style="`width:${widths}px`">
-          <el-card ref="card" v-if="info.year">
-            <calendar :year="`${info.year}`" :selfBtn="selfBtn" @draft="selectDate" @eventClick="eventClick" :vacation="vacation" :events="events"></calendar>
-          </el-card>
-        </el-col>
-      </el-row>
-      <template v-else-if="view == 'school'">
-        <sch-arr :events="events" :year="info.year" :template="template" @toSave="toSave" @toDirector="toDirector"></sch-arr>
-      </template>
-      <template v-else>
-        <dir-arr :events="events"></dir-arr>
-      </template>
-    </detail-frame>
+        </el-col> -->
+      <el-col :span="24" :style="`overflow:auto`">
+        <el-card ref="card" v-if="info.year" height="800px">
+          <calendar
+            :year="`${info.year || '2020'}`"
+            :selfBtn="selfBtn"
+            @draft="selectDate"
+            @eventClick="eventClick"
+            :vacation="vacation"
+            :events="events"
+          ></calendar>
+        </el-card>
+      </el-col>
+    </el-row>
+    <template v-else-if="view == 'school'">
+      <sch-arr :events="events" :year="info.year" :template="template" @toSave="toSave" @toDirector="toDirector"></sch-arr>
+    </template>
+    <template v-else>
+      <dir-arr :events="events"></dir-arr>
+    </template>
+    <!-- </detail-frame> -->
     <el-drawer :visible.sync="drawer" direction="rtl" title="安排计划" @close="toClose">
       <event
         :data="form"
@@ -93,23 +95,33 @@ var moment = require('moment');
 import detailFrame from '@frame/layout/admin/detail-frame';
 import calendar from '@frame/components/calendar';
 import dataTable from '@frame/components/data-table';
-import event from './parts/event';
-import schArr from './parts/school-arrange';
-import dirArr from './parts/director-arrange';
+import event from '../parts/event';
+import schArr from '../parts/school-arrange';
+import dirArr from '../parts/director-arrange';
 import { mapState, createNamespacedHelpers } from 'vuex';
 const { mapActions } = createNamespacedHelpers('trainplan');
 const { mapActions: trainTemplate } = createNamespacedHelpers('trainTemplate');
 const { mapActions: schPlan } = createNamespacedHelpers('schPlan');
+const { mapActions: util } = createNamespacedHelpers('util');
 export default {
   name: 'arrange',
   props: {},
-  components: { detailFrame, calendar, dataTable, event, schArr, dirArr },
+  components: {
+    // detailFrame,
+    calendar,
+    // dataTable,
+    event,
+    schArr,
+    dirArr,
+  },
   data: function() {
     var that = this;
     return {
       view: 'plan',
       template: {},
-      info: {},
+      info: {
+        year: '2020',
+      },
       form: {},
       selectList: [],
       events: [],
@@ -150,15 +162,14 @@ export default {
           click: () => (that.dialog = true),
           position: 'left',
         },
-        school: {
-          text: '学校安排',
-          click: () => that.changeView(),
-          position: 'left',
+        plan: {
+          text: '保存培训计划',
+          click: () => that.savePlan(),
+          position: 'right',
         },
       },
       input: {
-        term: 5,
-        start: '2020-01-01',
+        term: 0,
       },
       pickerOptions: {
         disabledDate: time => that.checkDate(time),
@@ -171,14 +182,16 @@ export default {
   },
   created() {
     this.search();
-    this.searchTemplate();
   },
   methods: {
     ...mapActions(['fetch', 'update']),
     ...trainTemplate({ trainTemplate: 'query' }),
     ...schPlan({ setSchPlan: 'schArrange' }),
+    ...util({ modelFetch: 'fetch' }),
     async search() {
-      const res = await this.fetch(this.id);
+      let planid = _.get(this.defaultOption, 'planid');
+      if (!planid) return;
+      const res = await this.fetch(planid);
       if (this.$checkRes(res)) {
         let fest = _.get(res.data, 'festivals', []);
         let vac = fest.map(i => {
@@ -218,6 +231,7 @@ export default {
         this.$set(this, `events`, events);
         this.$set(this, `selectList`, events);
       }
+      this.searchTemplate();
     },
     //模板事件开始
     //生成默认模板
@@ -344,7 +358,7 @@ export default {
       this.toClose();
     },
     //计划保存
-    savePlan(is_return) {
+    savePlan() {
       let data = JSON.parse(JSON.stringify(this.info));
       let plan = JSON.parse(JSON.stringify(this.selectList));
       let termnum = [];
@@ -379,10 +393,7 @@ export default {
       let msg;
       res = this.update(data);
       msg = `计划保存成功`;
-      if (this.$checkRes(res, msg)) {
-        if (is_return) this.$router.push({ path: './index' });
-        else this.search();
-      }
+      this.$checkRes(res, msg);
     },
     //选择时间的事件
     selectDate(object) {
@@ -408,7 +419,9 @@ export default {
     },
     //
     async searchTemplate() {
-      let res = await this.trainTemplate();
+      let planid = _.get(this.defaultOption, 'planid');
+      let planyearid = _.get(this.defaultOption, 'planyearid');
+      let res = await this.modelFetch({ model: 'trainmodel', planyearid, planid });
       if (this.$checkRes(res)) {
         if (res.data.length > 0) {
           this.$set(this, `template`, res.data[0]);
@@ -441,15 +454,15 @@ export default {
       let res = moment(date).isBetween(`${year}-01-01`, `${year}-12-31`, null, '[]');
       return !res;
     },
-    returns() {
-      if (this.view == 'plan') this.$router.push({ path: './index' });
-      else if (this.view == 'school') this.view = 'plan';
-      else this.view = 'school';
-    },
-    changeView() {
-      this.savePlan();
-      this.view = 'school';
-    },
+    // returns() {
+    //   if (this.view == 'plan') this.$router.push({ path: './index' });
+    //   else if (this.view == 'school') this.view = 'plan';
+    //   else this.view = 'school';
+    // },
+    // changeView() {
+    //   this.savePlan();
+    //   this.view = 'school';
+    // },
     async toSave(schPlan) {
       const res = await this.setSchPlan(schPlan);
       if (this.$checkRes(res)) {
@@ -460,8 +473,16 @@ export default {
       this.view = 'director';
     },
   },
+  watch: {
+    defaultOption: {
+      handler(val) {
+        this.search();
+      },
+      deep: true,
+    },
+  },
   computed: {
-    ...mapState(['user']),
+    ...mapState(['user', 'defaultOption']),
     id() {
       return this.$route.query.id;
     },
@@ -472,7 +493,7 @@ export default {
       return `${this.$route.meta.title}`;
     },
     widths() {
-      let width = (document.body.clientWidth - 200) * 0.5;
+      let width = (document.body.clientWidth - 200) * 0.65;
       return width > 400 ? width : 400;
     },
   },
@@ -482,4 +503,9 @@ export default {
 };
 </script>
 
-<style lang="less" scoped></style>
+<style lang="less" scoped>
+/deep/.el-card__body {
+  overflow: auto;
+  padding: 10px;
+}
+</style>

+ 174 - 0
src/views/new-plan/arrange/director-arrange.vue

@@ -0,0 +1,174 @@
+<template>
+  <div id="director-arrange">
+    <detail-frame :title="pageTitle">
+      <el-row type="flex" align="middle" justify="end" style="padding-bottom:10px">
+        <el-col :span="2">
+          <el-button type="success" size="mini" @click="toSave">保存班主任计划</el-button>
+        </el-col>
+        <el-col :span="2">
+          <el-button type="primary" size="mini" @click="toArrange">一键分配</el-button>
+        </el-col>
+      </el-row>
+      <data-table v-loading="loading" :fields="fields" :data="classList" :opera="opera" @edit="toEdit" :toFormat="toFormat"></data-table>
+    </detail-frame>
+    <el-dialog title="选择班主任" :visible.sync="dialog" width="20%">
+      <data-form :data="form" :fields="fields" :rules="{}" @save="handleSave">
+        <template #options="{item,form}">
+          <template v-if="item.model == 'headteacherid'">
+            <el-option-group v-for="(dept, index) in htList" :label="dept.name" :key="index">
+              <el-option v-for="(i, tIndex) in dept.list" :key="`${index}-${tIndex}`" :label="i.name" :value="i._id"></el-option>
+            </el-option-group>
+          </template>
+        </template>
+      </data-form>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash';
+import detailFrame from '@frame/layout/admin/detail-frame';
+import dataTable from '@frame/components/data-table';
+import dataForm from '@frame/components/form';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions } = createNamespacedHelpers('teaPlan');
+const { mapActions: classes } = createNamespacedHelpers('classes');
+const { mapActions: mapDept } = createNamespacedHelpers('dept');
+const { mapActions: director } = createNamespacedHelpers('director');
+export default {
+  name: 'director-arrange',
+  props: {
+    // events: { type: Array, default: () => [] },
+  },
+  components: { dataTable, dataForm, detailFrame },
+  data: function() {
+    return {
+      loading: true,
+      dialog: false,
+      form: {},
+      classList: [],
+      htList: [],
+      dirList: [],
+      deptList: [],
+      fields: [
+        { label: '期', prop: 'term', model: 'term', type: 'text' },
+        { label: '批', prop: 'batch', model: 'batch', type: 'text' },
+        { label: '班级', prop: 'name', model: 'name', type: 'text' },
+        { label: '班主任', prop: 'headteacherid', model: 'headteacherid', type: 'select', format: true }, //
+      ],
+      opera: [
+        {
+          label: '选择班主任',
+          icon: 'el-icon-edit',
+          method: 'edit',
+        },
+      ],
+    };
+  },
+  async created() {
+    await this.getOtherList();
+    this.search();
+  },
+  methods: {
+    ...director({ getDirList: 'query' }),
+    ...mapDept({ getDept: 'query' }),
+    ...mapActions(['divide', 'findTeacher']),
+    ...classes(['query', 'upHeadTea']),
+    async search() {
+      let planid = _.get(this.defaultOption, 'planid');
+      if (!planid) return;
+      let res = await this.query({ planid });
+      if (this.$checkRes(res)) {
+        //现在应该不需要过滤班的batch和term了,返回应该有
+        // let arr = res.data.map(i => {
+        //   let e = this.events.find(f => f.termid == i.termid && f._id == i.batchid);
+        //   if (e) {
+        //     i.term = _.get(e, `term`);
+        //     i.batch = _.get(e, `batch`);
+        //   }
+        //   return i;
+        // });
+        // arr = _.reverse(arr.sort((a, b) => a.term - b.term && a.batch - b.batch));
+        // this.$set(this, `classList`, arr);
+        this.loading = false;
+      }
+    },
+    async toArrange() {
+      this.loading = true;
+      let res = await this.divide({ trainplanid: this.id });
+      if (this.$checkRes(res)) {
+        let arr = this.classList.map(i => {
+          let r = res.data.find(f => f.classid == i._id);
+          if (r) i.headteacherid = r.headteacherid;
+          return i;
+        });
+        this.$set(this, `classList`, arr);
+      }
+      this.loading = false;
+    },
+    async toEdit({ data, index }) {
+      let res = await this.findTeacher({ planid: data.planid, termid: data.termid, batchid: data.batchid });
+      if (this.$checkRes(res)) {
+        let group = _.groupBy(res.data, 'department');
+        let keys = Object.keys(group);
+        let arr = keys.map(key => {
+          let r = this.deptList.find(f => f.id == key);
+          let obj = {};
+          if (r) {
+            obj.name = r.name;
+            obj.list = group[key];
+          }
+          return obj;
+        });
+        this.$set(this, `htList`, arr);
+      }
+      this.dialog = true;
+      this.$set(this, `form`, { ...JSON.parse(JSON.stringify(data)), index });
+    },
+    handleSave({ data }) {
+      let { index, ...info } = data;
+      this.$set(this.classList, index, info);
+      this.dialog = false;
+    },
+    async toSave() {
+      let data = JSON.parse(JSON.stringify(this.classList));
+      let res = await this.upHeadTea(data);
+      this.$checkRes(res, '保存成功', res.errmsg || '保存失败');
+    },
+    async getOtherList() {
+      let res = await this.getDept();
+      if (this.$checkRes(res)) this.$set(this, `deptList`, res.data);
+      res = await this.getDirList();
+      if (this.$checkRes(res)) this.$set(this, `dirList`, res.data);
+    },
+    toFormat({ model, value }) {
+      if (model == 'headteacherid') {
+        let r = this.dirList.find(f => f._id == value);
+        if (r) return r.name;
+      }
+    },
+  },
+  watch: {
+    defaultOption: {
+      handler(val) {
+        this.search();
+      },
+      deep: true,
+    },
+  },
+  computed: {
+    ...mapState(['user', 'defaultOption']),
+    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>

+ 583 - 0
src/views/new-plan/arrange/school-arrange.vue

@@ -0,0 +1,583 @@
+<template>
+  <div id="school-arrange">
+    <el-row type="flex" align="middle" justify="end" style="padding-bottom:10px">
+      <el-col :span="2">
+        <el-button type="success" size="mini" @click="toSave">保存计划</el-button>
+      </el-col>
+      <el-col :span="2">
+        <el-button type="primary" size="mini" @click="toArrange">一键分配</el-button>
+      </el-col>
+      <el-col :span="2">
+        <el-button type="primary" size="mini" plain @click="dialog = true">查看汇总</el-button>
+      </el-col>
+      <el-col :span="2">
+        <el-button size="mini" icon="el-icon-right" @click="toDirector">班主任计划</el-button>
+      </el-col>
+    </el-row>
+
+    <!-- 大表 -->
+    <el-card v-loading="!already" style="min-height:500px">
+      <el-table
+        :data="list"
+        border
+        stripe
+        size="mini"
+        height="650px"
+        @cell-click="cellClick"
+        :cell-style="cellStyle"
+        :cell-class-name="cellClass"
+        v-if="already"
+      >
+        <el-table-column label="学校" fixed align="center" prop="name" width="180">
+          <template v-slot="{ row }">
+            <el-row>
+              <el-col :span="24">
+                {{ row.name }}
+              </el-col>
+              <el-col :span="24" v-if="row.number"> 名额:{{ row.number }} </el-col>
+            </el-row>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" v-for="(i, index) in termList" :key="index" :label="`第${i.term}期`">
+          <el-table-column align="center" width="95" :prop="`term${index + 1}`">
+            <template #header>
+              <el-row>
+                <el-col :span="24">{{ i.start }}</el-col>
+                <el-col :span="24">至</el-col>
+                <el-col :span="24">{{ i.end }}</el-col>
+              </el-row>
+            </template>
+          </el-table-column>
+        </el-table-column>
+      </el-table>
+    </el-card>
+
+    <el-drawer :visible.sync="drawer" direction="rtl" title="名额分配" @close="toClose">
+      <el-row type="flex" align="middle" justify="center" style="padding:20px" :gutter="20">
+        <el-col :span="12">
+          <el-card>
+            <el-form size="mini">
+              <el-form-item label="学校">{{ form | getProp('sch.name') }}</el-form-item>
+              <el-form-item label="学校层次">{{ form | getProp('sch.level') }}</el-form-item>
+              <el-form-item label="需要派车">{{ form | getProp('sch.hascar') }}</el-form-item>
+              <el-form-item label="总名额">{{ form | getProp('sch.number') }}</el-form-item>
+              <el-form-item label="剩余名额">{{ form | getProp('sch.remaining') }}</el-form-item>
+            </el-form>
+          </el-card>
+        </el-col>
+        <el-col :span="12">
+          <el-card>
+            <el-form size="mini">
+              <el-form-item label="期数">{{ form | getProp('term.term') }}</el-form-item>
+              <el-form-item label="开始时间">{{ form | getProp('term.start') }}</el-form-item>
+              <el-form-item label="结束时间">{{ form | getProp('term.end') }}</el-form-item>
+              <el-form-item label="总名额">{{ form | getProp('term.tpt') }}</el-form-item>
+              <el-form-item label="剩余名额">{{ form | getProp('term.remaining') }}</el-form-item>
+            </el-form>
+          </el-card>
+        </el-col>
+      </el-row>
+      <el-card style="padding:20px">
+        <el-row type="flex" align="middle" justify="center" :gutter="20">
+          <el-col :span="24">
+            <el-form size="mini" label-width="150px">
+              <el-form-item label="分配给该期的名额">
+                <el-input-number
+                  v-model="form.num"
+                  type="number"
+                  placeholder="请输入分配给该期的名额"
+                  :max="dGetMax(form)"
+                  :min="0"
+                  style="width:250px"
+                  @change="dComputed"
+                ></el-input-number>
+              </el-form-item>
+              <el-form-item>
+                <el-row type="flex" align="middle" justify="start" :gutter="20">
+                  <el-col :span="4">
+                    <el-button type="primary" @click="toSend">分配</el-button>
+                  </el-col>
+                  <el-col :span="4">
+                    <el-button @click="toClose">取消</el-button>
+                  </el-col>
+                </el-row>
+              </el-form-item>
+            </el-form>
+          </el-col>
+        </el-row>
+      </el-card>
+    </el-drawer>
+
+    <!-- 数据汇总 -->
+    <el-dialog :visible.sync="dialog" title="数据汇总">
+      <el-collapse v-model="activeName" accordion>
+        <el-collapse-item :title="`各期情况 (${isOk() ? '已完成' : '未完成'})`" name="1">
+          <el-table :data="totalList" border stripe size="mini">
+            <el-table-column prop="type"></el-table-column>
+            <el-table-column v-for="(i, index) in termList" align="center" :key="index" :label="`第${i.term}期`" :prop="`term${i.term}`"> </el-table-column>
+          </el-table>
+        </el-collapse-item>
+        <el-collapse-item :title="`车辆统计 共计(${getAllCarTotal()})`" name="2">
+          <el-table :data="carList" border stripe size="mini" height="500px">
+            <el-table-column type="expand">
+              <template v-slot="{ row }">
+                <el-row>
+                  <el-col :span="24" v-for="(i, index) in row.carTerm" :key="index">
+                    <span v-if="i.num > 0">第 {{ i.term }} 期需要 {{ i.num }} 辆车</span>
+                  </el-col>
+                </el-row>
+              </template>
+            </el-table-column>
+            <el-table-column label="学校" align="center" prop="name" width="180"></el-table-column>
+            <el-table-column label="车辆需求总数" align="center">
+              <template v-slot="{ row }">
+                {{ getTableCarTotal(row.carTerm) }}
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-collapse-item>
+      </el-collapse>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash';
+var moment = require('moment');
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions } = createNamespacedHelpers('trainplan');
+const { mapActions: trainTemplate } = createNamespacedHelpers('trainTemplate');
+const { mapActions: school } = createNamespacedHelpers('school');
+const { mapActions: schPlan } = createNamespacedHelpers('schPlan');
+export default {
+  name: 'school-arrange',
+  props: {
+    events: { type: Array, default: () => [] },
+    year: { type: String, default: `${new Date().getFullYear()}` },
+    template: { type: Object, default: () => {} },
+  },
+  components: {},
+  data: () => {
+    return {
+      list: [],
+      termList: [],
+      totalList: [],
+      carList: [],
+      dialog: false,
+      drawer: false,
+      form: {},
+      activeName: '1',
+      already: false,
+    };
+  },
+  async created() {
+    await this.getSchool();
+  },
+  methods: {
+    ...school(['query']),
+    ...schPlan({ schPlanQuery: 'query', createSchPlan: 'create', updateSchPlan: 'update' }),
+    //请求,处理学校列表
+    async getSchool() {
+      const res = await this.query();
+      if (this.$checkRes(res)) {
+        let school = res.data.map(i => {
+          i = _.omit(i, ['meta', 'id', '_id', 'logourl']);
+          if (i.number) i.remaining = JSON.parse(JSON.stringify(i.number));
+          else i.remaining = 0;
+          return i;
+        });
+        this.$set(this, `list`, school);
+        await this.getSchoolPlan();
+        await this.getTotal();
+      }
+    },
+    //自动安排
+    toArrange() {
+      this.toReset();
+      let school = this.list.filter(f => f.daterange);
+      //整理之后的学校列表
+      school = _.reverse(_.sortBy(school, ['level', 'hascar']));
+      //整理每个学校的daterange:[{start,end}]的形式
+      school = this.changeRange(school);
+      let termList = JSON.parse(JSON.stringify(this.termList));
+      for (const sch of school) {
+        for (const t of termList) {
+          if (t.remaining && t.remaining <= 0) continue;
+          let { result } = this.$tqInRange(t.start, t.end, sch.daterange);
+          //result为true:说明该期复合要求
+          if (result) {
+            if (sch.remaining > t.remaining) {
+              //学校名额>本期剩余名额:
+              //学校剩余名额 = 学校剩余名额(原) - 本期剩余名额
+              //学校在该期的名额数量 = 本期剩余名额
+              //本期剩余名额=0;
+              sch.remaining = sch.remaining - t.remaining;
+              sch[`term${t.term}`] = t.remaining;
+              sch[`id_term${t.term}`] = t.termid;
+              t.remaining = 0;
+              continue;
+            } else if (sch.remaining <= t.remaining) {
+              //学校名额<=本期剩余名额:
+              //本期剩余名额 = 本期剩余名额(原) - 学校剩余名额;
+              //学校在该期的名额数量 = 学校剩余名额
+              //学校剩余名额 = 0
+              t.remaining = t.remaining - sch.remaining;
+              sch[`term${t.term}`] = sch.remaining;
+              sch[`id_term${t.term}`] = t.termid;
+              sch.remaining = 0;
+              break;
+            }
+          }
+        }
+        let index = _.findIndex(this.list, f => f.code == sch.code);
+        if (index >= 0) this.$set(this.list, index, sch);
+      }
+      this.listClear();
+      this.$set(this, `termList`, termList);
+      this.getTotal();
+      this.getCarTotal();
+    },
+    //保存整体计划
+    toSave() {
+      //修改学校上报计划,整理数据
+      let schPlan = this.list.map(i => {
+        let plan = _.cloneDeep(i.plan);
+        let keys = Object.keys(i).filter(f => _.startsWith(f, 'term'));
+        for (const key of keys) {
+          let object = {};
+          object.termnum = key.match(/\d+(.\d+)?/g)[0];
+          object._id = i[`_id_term${object.termnum}`];
+          object.number = i[`term${object.termnum}`];
+          object.termid = i[`id_term${object.termnum}`];
+          if (i.carTerm) {
+            let car = i.carTerm.find(f => f.term == object.termnum);
+            if (car) object.carnum = car.num;
+          }
+          plan.term.push(object);
+        }
+        if (plan) return plan;
+      });
+      schPlan = _.compact(schPlan);
+      schPlan = schPlan.map(i => {
+        i.term = _.uniqBy(this.checkTerm(i.term), '_id');
+        return i;
+      });
+      this.$emit('toSave', schPlan);
+    },
+    //手动更改
+    cellClick(row, column) {
+      //获取指定期
+      let term = this.termList.find(f => f.term == column.property.match(/\d+(.\d+)?/g)[0]);
+      //学校上报时间转换
+      row = this.changeRange(row);
+      let { result } = this.$tqInRange(term.start, term.end, row.daterange);
+      if (result) {
+        this.form.term = JSON.parse(JSON.stringify(term));
+        this.form.sch = JSON.parse(JSON.stringify(row));
+        this.$set(this.form, `num`, row[`term${term.term}`] * 1 || 0);
+        this.drawer = true;
+        //可以输入,整理显示数据
+      } else this.$message.error(`第 ${term.term} 期(${term.start} 至 ${term.end})不在 ${row.name} 上报的时间内!`);
+    },
+    toSend() {
+      let num = _.get(this.form, 'num');
+      let term = _.get(this.form, 'term');
+      let sch = _.get(this.form, 'sch');
+      let tn = term.term;
+      sch[`term${tn}`] = num;
+      sch[`id_term${tn}`] = term.termid;
+      // sch[`remaining`] = sch.remaining;
+      // term.remaining = term.remaining;
+      let si = _.findIndex(this.list, f => f.code == sch.code);
+      let ti = _.findIndex(this.termList, f => f.term == term.term);
+      this.$set(this.list, si, sch);
+      this.$set(this.termList, ti, term);
+      this.toClose();
+      this.getTotal();
+      this.getCarTotal();
+    },
+    //将学校上报的月份数组[String]=>[{start,end}]方法
+    changeRange(data) {
+      let c = r => {
+        return r.map(mon => {
+          if (_.isObject(mon)) return mon;
+          let start = moment()
+            .year(this.year)
+            .month(mon * 1 - 1)
+            .date('1')
+            .format('YYYY-MM-DD');
+          let end = moment()
+            .year(this.year)
+            .month(mon * 1)
+            .date('1')
+            .subtract(1, 'days')
+            .format('YYYY-MM-DD');
+          return { start, end };
+        });
+      };
+      let duplicate = JSON.parse(JSON.stringify(data));
+      if (_.isArray(duplicate)) {
+        duplicate = duplicate.map(i => {
+          let daterange = _.get(i, `daterange`, []); //['4','5','6']形式
+          i.daterange = c(daterange);
+          return i;
+        });
+      } else if (_.isObject(duplicate)) {
+        duplicate.daterange = c(duplicate.daterange);
+      }
+
+      return duplicate;
+    },
+    //学校匹配学校计划
+    async getSchoolPlan() {
+      const res = await this.schPlanQuery({ planid: this.id });
+      if (this.$checkRes(res)) {
+        let nl = this.list.map(i => {
+          let r = res.data.find(f => f.schid == i.code);
+          if (r) {
+            i.daterange = r.daterange;
+            i = this.changeRange(i);
+            let term = _.get(r, `term`);
+            term = this.checkTerm(term);
+            if (term) {
+              i.carTerm = [];
+              for (const t of term) {
+                i[`term${t.termnum}`] = t.number;
+                i[`id_term${t.termnum}`] = t.termid;
+                i[`_id_term${t.termnum}`] = t._id;
+                i.carTerm.push({ term: t.termnum, num: t.carnum });
+              }
+              i.remaining = i.remaining - term.reduce((prev, next) => prev + (next.number * 1 || 0), 0);
+            }
+            i.plan = r;
+          }
+          return i;
+        });
+        this.$set(this, `list`, nl);
+      }
+      this.$set(this, `already`, true);
+    },
+    //检查已分配的数据是否与现在的期对应
+    checkTerm(term) {
+      let res = term.map(i => {
+        let r = this.termList.find(f => f.termid == i.termid);
+        if (r) return i;
+      });
+      res = _.compact(res);
+      return res;
+    },
+    //整理期(事件)列表
+    getTermList() {
+      let arr = _.toPairs(_.groupBy(this.events, 'termid'));
+      let res = arr.map(i => {
+        i = _.flatten(_.remove(i, r => _.isArray(r)));
+        let tpt = i.reduce((prev, next) => {
+          let num = next.number ? next.number * 1 * next.class : next.class * _.get(this.template, 'stunum', 0);
+          return prev + num;
+        }, 0);
+        let { start, term, termid } = _.head(i);
+        let { end } = _.last(i);
+        return { start, end, term, tpt, remaining: tpt, termid }; //tpt:该期总人数 ,remaining:剩余人数
+      });
+      this.$set(this, `termList`, res);
+    },
+    //名额统计
+    getTotal() {
+      let res = this.termList.map((i, index) => {
+        i.total = this.list.reduce((prev, next) => prev + (next[`term${i.term}`] * 1 || 0), 0);
+        return i;
+      });
+      let aObject = { type: '已分配' };
+      let rObject = { type: '未分配' };
+      res.map(i => {
+        aObject[`term${i.term}`] = i.total || 0;
+        i.remaining = i.tpt - i.total || 0;
+        rObject[`term${i.term}`] = i.remaining || 0;
+      });
+      this.$set(this, `totalList`, [aObject, rObject]);
+    },
+    //车辆统计
+    getCarTotal() {
+      let school = this.list.filter(f => f.hascar == '1');
+      let carpnum = this.template.carpnum;
+      school.map(sch => {
+        let keys = Object.keys(sch).filter(f => _.startsWith(f, 'term'));
+        let pt = 0; //人数总数
+        sch.carTerm = [];
+        for (const key of keys) {
+          let term = key.match(/\d+(.\d+)?/g)[0];
+          let num = Math.ceil(sch[key] / carpnum);
+          sch.carTerm.push({ term, num });
+        }
+      });
+      this.$set(this, `carList`, school);
+    },
+    //计算车辆
+    getTableCarTotal(data) {
+      return data.reduce((prev, next) => {
+        return prev + (next.num || 0);
+      }, 0);
+    },
+    //所有车辆和计算
+    getAllCarTotal() {
+      return this.carList.reduce((prev, next) => {
+        let num = next.carTerm.reduce((p, n) => p + (n.num || 0), 0);
+        return prev + num;
+      }, 0);
+    },
+    //是否分配完毕,按期来说
+    isOk() {
+      let res = true;
+      let obj = this.totalList[1];
+      if (obj) {
+        let keys = Object.keys(obj).filter(f => f != 'type');
+        for (const key of keys) {
+          if (obj[key] > 0) {
+            res = false;
+            break;
+          }
+        }
+      } else res = false;
+
+      return res;
+    },
+    //分配重置
+    async toReset() {
+      this.getTermList();
+      let list = this.list.map(i => {
+        let object = _.pick(i, ['address', 'code', 'daterange', 'hascar', 'level', 'name', 'shortname', 'number', 'plan']);
+        object.remaining = object.number;
+        return object;
+      });
+      this.$set(this, `list`, list);
+    },
+    //将分配为0的数据清除
+    listClear() {
+      let list = this.list.map(i => {
+        let keys = Object.keys(i).filter(f => _.startsWith(f, 'term'));
+        for (const key of keys) {
+          if (i[key] == 0) {
+            delete i[key];
+            delete i[`id_${key}`];
+          }
+        }
+        return i;
+      });
+      this.$set(this, `list`, list);
+    },
+    //抽屉名额响应式计算
+    dComputed(cv, ov) {
+      let sch = this.form.sch;
+      let term = this.form.term;
+      let keys = Object.keys(sch)
+        .filter(f => _.startsWith(f, 'term'))
+        .filter(f => f != `term${term.term}`);
+      let sr = keys.reduce((prev, next) => prev - sch[next], sch.number) - cv;
+      let tr = this.list.reduce((prev, next) => {
+        if (next.code != sch.code) return prev - (next[`term${term.term}`] * 1 || 0);
+        else return prev;
+      }, term.tpt);
+      tr = tr - cv;
+      this.$set(this.form.sch, `remaining`, sr);
+      this.$set(this.form.term, `remaining`, tr);
+    },
+    //计算抽屉最大值
+    dGetMax(data) {
+      if (!this.drawer) return 0;
+      let res = 0;
+      let t = this.termList.find(f => f.termid == this.form.term.termid);
+      let sch = this.list.find(f => f.code == this.form.sch.code);
+      let s = _.get(data, 'sch');
+      let tr = _.get(data, 'term.remaining', 0) * 1;
+      let sr = _.get(data, 'sch.remaining', 0) * 1;
+      let num = this.form.num;
+      if (s.remaining == 0) return num;
+      if (tr > sr) {
+        res = sch.number * 1 || 0;
+      } else if (tr == sr) {
+        let keys = Object.keys(s)
+          .filter(f => _.startsWith(f, 'term'))
+          .filter(f => f != `term${t.term}`);
+        let nsr = keys.reduce((prev, next) => prev - s[next], s.number);
+        res = nsr;
+      } else {
+        res = t.tpt * 1 - t.total * 1 || 0;
+      }
+      return res * 1;
+    },
+    //关闭抽屉
+    toClose() {
+      this.drawer = false;
+      this.form = {};
+    },
+    //单元格样式
+    cellStyle({ row, column, rowIndex, columnIndex }) {
+      let disabled = { background: '#F56C6C' }; //{ background: '#F56C6C' }  ' disabled'
+      if (column.property == 'name') return '';
+      if (!_.get(row, 'daterange')) return disabled;
+      let term = this.termList.find(f => f.term == column.property.match(/\d+(.\d+)?/g)[0]);
+      //学校上报时间转换
+      row = this.changeRange(row);
+      let { result } = this.$tqInRange(term.start, term.end, row.daterange);
+      if (!result) return disabled;
+      else return '';
+    },
+    cellClass({ row, column, rowIndex, columnIndex }) {
+      let disabled = ' disabled_point';
+      if (column.property == 'name') return '';
+      if (!_.get(row, 'daterange')) return disabled;
+      let term = this.termList.find(f => f.term == column.property.match(/\d+(.\d+)?/g)[0]);
+      //学校上报时间转换
+      row = this.changeRange(row);
+      let { result } = this.$tqInRange(term.start, term.end, row.daterange);
+      if (!result) return disabled;
+      else return '';
+    },
+    //班主任计划
+    toDirector() {
+      this.toSave();
+      this.$emit(`toDirector`);
+    },
+  },
+  filters: {
+    getProp(data, prop) {
+      if (prop.includes('hascar')) {
+        let res = _.get(data, prop);
+        return res == '1' ? '需要' : '不需要';
+      } else return _.get(data, prop);
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+    id() {
+      return this.$route.query.id;
+    },
+  },
+  watch: {
+    events: {
+      handler(val) {
+        if (val.length > 0) this.getTermList();
+      },
+      deep: true,
+      immediate: true,
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.el-table {
+  overflow: visible !important;
+}
+</style>
+<style>
+.disabled_point {
+  cursor: not-allowed;
+}
+</style>

+ 93 - 0
src/views/new-plan/arrange/school-num.vue

@@ -0,0 +1,93 @@
+<template>
+  <div id="school-num">
+    <detail-frame :title="pageTitle">
+      <data-table ref="table" :fields="fields" :data="list" :opera="opera" :total="total" @edit="toEdit" @query="search"></data-table>
+    </detail-frame>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash';
+import detailFrame from '@frame/layout/admin/detail-frame';
+import dataTable from '@frame/components/filter-page-table';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: plan } = createNamespacedHelpers('trainplan');
+const { mapActions: school } = createNamespacedHelpers('school');
+export default {
+  name: 'school-num',
+  props: {},
+  components: { detailFrame, dataTable },
+  data: function() {
+    return {
+      list: [],
+      plan: {},
+      opera: [
+        {
+          label: '修改',
+          icon: 'el-icon-edit',
+          method: 'edit',
+        },
+        {
+          label: '删除',
+          icon: 'el-icon-delete',
+          method: 'delete',
+          confirm: true,
+        },
+        {
+          label: '年度计划管理',
+          icon: 'el-icon-date',
+          method: 'plan',
+        },
+      ],
+      fields: [
+        { label: '学校', prop: 'name' },
+        { label: '人数', prop: 'num' },
+      ],
+      total: 0,
+    };
+  },
+  created() {
+    this.searchPlan();
+    this.search();
+  },
+  methods: {
+    ...plan({ getPlan: 'fetch' }),
+    ...school(['query']),
+    async searchPlan() {
+      let planid = _.get(this.defaultOption, 'planid');
+      if (!planid) return;
+      let res = await this.getPlan(planid);
+      if (this.$checkRes(res)) this.$set(this, 'plan', res.data);
+    },
+    async search({ skip = 0, limit = 10, ...info } = {}) {
+      let res = await this.query({ skip, limit, ...info });
+      if (this.$checkRes(res)) {
+        //TODO 查出这页的学校分配的人数,放到学校里面,表格输出
+        this.$set(this, `list`, res.data);
+        this.$set(this, `total`, res.total);
+      }
+    },
+    async toEdit({ data }) {},
+  },
+  watch: {
+    defaultOption: {
+      async handler(val) {
+        await this.searchPlan();
+        this.$refs.table.changePage();
+      },
+      deep: true,
+    },
+  },
+  computed: {
+    ...mapState(['user', 'defaultOption']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 42 - 5
src/views/new-plan/index.vue

@@ -1,13 +1,13 @@
 <template>
   <div id="index">
-    <list-frame :title="pageTitle" @query="search" :total="total" :needFilter="false" @add="toAdd">
-      <data-table :fields="fields" :data="list" :opera="opera" @edit="toEdit" @delete="toDelete" @date="date"></data-table>
+    <list-frame :title="pageTitle" @query="search" :total="total" :needFilter="false" @add="toAdd" :returns="returns">
+      <data-table :fields="fields" :data="list" :opera="opera" @set="toSet" @edit="toEdit" @delete="toDelete" @date="date"></data-table>
     </list-frame>
     <el-dialog title="年度信息" width="30%" :visible.sync="dialog" center :destroy-on-close="true" @close="handleClose" :reset="false">
       <data-form :data="info" :fields="Ffields" :rules="rules" @save="handleSave" :isNew="!info.id" :styles="{ padding: 0 }" labelWidth="60px">
         <template #custom="{item,form}">
           <template v-if="item.model == 'festivals'">
-            <data-table :fields="festFields" :data="form[item.model]" :opera="opera" @edit="toFestEdit" @delete="toFestDelete"></data-table>
+            <data-table :fields="festFields" :data="form[item.model]" :opera="formOpera" @edit="toFestEdit" @delete="toFestDelete"></data-table>
           </template>
         </template>
         <template #radios="{item,form}">
@@ -33,6 +33,7 @@ import dataForm from '@frame/components/form';
 import { mapState, createNamespacedHelpers } from 'vuex';
 const { mapActions: trainplan } = createNamespacedHelpers('trainplan');
 const { mapActions: other } = createNamespacedHelpers('other');
+const { mapActions: setting } = createNamespacedHelpers('setting');
 export default {
   name: 'index',
   props: {},
@@ -50,6 +51,7 @@ export default {
         {
           label: '计划安排',
           icon: 'el-icon-date',
+          display: i => i.status == 1,
           method: 'date',
         },
         {
@@ -57,6 +59,25 @@ export default {
           icon: 'el-icon-delete',
           method: 'delete',
         },
+        {
+          label: '设置默认年度计划',
+          icon: 'el-icon-setting',
+          methodZh: '您确认设置该年度计划为默认查看?',
+          confirm: true,
+          method: 'set',
+        },
+      ],
+      formOpera: [
+        {
+          label: '编辑',
+          icon: 'el-icon-edit',
+          method: 'edit',
+        },
+        {
+          label: '删除',
+          icon: 'el-icon-delete',
+          method: 'delete',
+        },
       ],
       fields: [
         { label: '年度', prop: 'year' },
@@ -89,8 +110,9 @@ export default {
   methods: {
     ...other(['calendar']),
     ...trainplan(['query', 'create', 'delete', 'update']),
+    ...setting({ sFetch: 'fetch', sUpdate: 'update' }),
     async search({ skip = 0, limit = 10, ...info } = {}) {
-      const res = await this.query({ skip, limit, ...info });
+      const res = await this.query({ skip, limit, ...info, planyearid: this.id });
       if (this.$checkRes(res)) {
         this.$set(this, `list`, res.data);
         this.$set(this, `total`, res.total);
@@ -114,6 +136,7 @@ export default {
       let msg;
       if (isNew) {
         //create
+        data.planyearid = this.id;
         res = await this.create(data);
         msg = '添加成功';
       } else {
@@ -170,12 +193,26 @@ export default {
     date({ data }) {
       this.$router.push({ path: './arrange', query: { id: data.id } });
     },
+    async toSet({ data }) {
+      let planid = _.get(data, 'id');
+      let duplicate = _.cloneDeep(this.defaultOption);
+      duplicate.planyearid = this.id;
+      duplicate.planid = planid;
+      let res = await this.sUpdate(duplicate);
+      if (this.$checkRes(res, '设置成功', res.errmsg)) this.search();
+    },
+    returns() {
+      window.history.go(-1);
+    },
   },
   computed: {
-    ...mapState(['user']),
+    ...mapState(['user', 'defaultOption']),
     pageTitle() {
       return `${this.$route.meta.title}`;
     },
+    id() {
+      return this.$route.query.id;
+    },
   },
   metaInfo() {
     return { title: this.$route.meta.title };

+ 31 - 5
src/views/setting/system-setting.vue

@@ -1,7 +1,7 @@
 <template>
   <div id="system-setting">
     <detail-frame :title="pageTitle">
-      <data-form :data="form" :fields="fields" :rules="rules" @save="handleSave" :isNew="isNew"> </data-form>
+      <data-form :data="form" :fields="fields" :rules="rules" @save="handleSave" labelWidth="150px" :reset="false"> </data-form>
     </detail-frame>
   </div>
 </template>
@@ -10,6 +10,8 @@
 import detailFrame from '@frame/layout/admin/detail-frame';
 import dataForm from '@frame/components/form';
 import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: setting } = createNamespacedHelpers('setting');
+
 export default {
   name: 'system-setting',
   props: {},
@@ -18,17 +20,41 @@ export default {
     return {
       form: {},
       fields: [
-        { label: '系统邮箱', required: true, model: 'email' },
-        { label: '邮箱密码', required: true, model: 'emailpw', type: 'password' },
+        { label: '服务器邮箱', required: true, model: 'user_email' },
+        { label: '服务器邮箱授权码', required: true, model: 'auth_code' },
+        { label: '上午考勤开始时间', required: true, model: 'am_start', type: 'time' },
+        { label: '上午考勤结束时间', required: true, model: 'am_end', type: 'time' },
+        { label: '下午开始时间', required: true, model: 'pm_start', type: 'time' },
+        { label: '下午结束时间', required: true, model: 'pm_end', type: 'time' },
+        { label: '寝室开始时间', required: true, model: 'bd_start', type: 'time' },
+        { label: '寝室结束时间', required: true, model: 'bd_end', type: 'time' },
       ],
       rules: {
         email: [{ required: true, message: '请输入系统邮箱' }],
         emailpw: [{ required: true, message: '请输入系统邮箱密码' }],
+        am_start: [{ required: true, message: '请选择上午考勤开始时间' }],
+        am_end: [{ required: true, message: '请选择上午考勤结束时间' }],
+        pm_start: [{ required: true, message: '请选择下午开始时间' }],
+        pm_end: [{ required: true, message: '请选择下午结束时间' }],
+        bd_start: [{ required: true, message: '请选择寝室开始时间' }],
+        bd_end: [{ required: true, message: '请选择寝室结束时间' }],
       },
     };
   },
-  created() {},
-  methods: {},
+  created() {
+    this.search();
+  },
+  methods: {
+    ...setting(['fetch', 'update']),
+    async search() {
+      let res = await this.fetch();
+      if (this.$checkRes(res)) this.$set(this, `form`, res.data);
+    },
+    async handleSave({ data }) {
+      let res = await this.update(data);
+      this.$checkRes(res, `修改成功`, res.errmsg);
+    },
+  },
   computed: {
     ...mapState(['user']),
     pageTitle() {

+ 49 - 50
src/views/test/detail.vue

@@ -1,67 +1,66 @@
 <template>
-  <div id="index">
-    <el-row style="padding:10px;height:100vh">
-      <el-col :span="24" class="icon">
-        <el-image :src="question"></el-image>
-      </el-col>
-      <el-col :span="24" style="height:10vh;text-align:center">
-        您确定将您的微信号与本平台账号绑定吗?
-      </el-col>
-      <el-col :span="24" style="height:4rem">
-        <van-button type="info" style="width:100%" round @click="toUpdate()">确定</van-button>
-      </el-col>
-      <el-col :span="24">
-        <van-button plain style="width:100%" round @click="cancel()">取消</van-button>
-      </el-col>
-    </el-row>
+  <div id="detail">
+    <div ref="cal"></div>
   </div>
 </template>
 
 <script>
-import { createNamespacedHelpers } from 'vuex';
-const { mapActions } = createNamespacedHelpers('doctor');
+import { Calendar } from '@fullcalendar/core';
+import '@fullcalendar/core/main.css';
+import '@fullcalendar/daygrid/main.css';
+import interactionPlugin from '@fullcalendar/interaction';
+import dayGridPlugin from '@fullcalendar/daygrid';
+import { mapState, createNamespacedHelpers } from 'vuex';
 export default {
-  metaInfo: { title: '用户绑定' },
-  name: 'index',
+  name: 'detail',
   props: {},
   components: {},
-  data: () => ({
-    question: '',
-  }),
+  data: function() {
+    return {};
+  },
   created() {},
-  computed: {
-    openid() {
-      return this.$route.query.openid;
-    },
-    doctorid() {
-      return this.$route.query.doctorid;
-    },
+  mounted() {
+    this.init();
   },
   methods: {
-    // ...mapActions(['bind']),
-    async toUpdate() {
-      const res = await this.bind({ id: this.doctorid, openid: this.openid });
-      if (res.errcode === 0) {
-        this.$router.push({ path: '/', query: { openid: this.openid } });
-        this.$toast({ type: 'success', message: '绑定成功' });
-      } else {
-        this.$toast({ type: 'fail', message: res.errmsg });
-      }
+    init() {
+      let cal = this.$refs.cal;
+      var calendar = new Calendar(cal, {
+        plugins: [dayGridPlugin, interactionPlugin],
+        locale: 'zh-cn',
+        height: 700,
+        contentHeight: 600,
+        headers: {
+          left: 'prev',
+          center: 'title',
+          right: 'dayGridMonth,dayGridWeek, today ,next',
+        },
+        buttonText: {
+          today: '今天',
+          dayGridMonth: '月视图',
+          dayGridWeek: '周视图',
+          listMonth: '月列表',
+        },
+        selectable: true,
+        displayEventEnd: true,
+        displayEventTime: true,
+        editable: false,
+        droppable: false,
+        weekNumberCalculation: 'ISO',
+      });
+      calendar.render();
     },
-    cancel() {
-      window.history.go(-1);
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
     },
   },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
 };
 </script>
 
-<style lang="less" scoped>
-.icon {
-  height: 30vh;
-  text-align: center;
-}
-.icon .el-image {
-  width: 100px;
-  margin: 40px 0;
-}
-</style>
+<style lang="less" scoped></style>

+ 41 - 8
src/views/train-batch/index.vue

@@ -7,13 +7,13 @@
             <el-button type="primary" size="mini" @click="toAdd" icon="el-icon-plus">添加</el-button>
           </el-col>
         </el-row>
-        <data-table :fields="fields" :data="list" :opera="opera" :total="total" @edit="toEdit" @query="search"></data-table>
+        <data-table :fields="fields" :data="list" :opera="opera" :total="total" @edit="toEdit" @query="search" @delete="toDelete" @plan="toPlan"></data-table>
       </el-card>
       <el-card v-else>
         <template #header>
           <el-button icon="el-icon-arrow-left" type="text" @click="toBack">返回</el-button>
         </template>
-        <data-form :data="form" :fields="formFields" :rules="rules" @save="handleSave" :isNew="form.id"> </data-form>
+        <data-form :data="form" :fields="formFields" :rules="rules" @save="handleSave"> </data-form>
       </el-card>
     </listFrame>
   </div>
@@ -25,6 +25,7 @@ import listFrame from '@frame/layout/admin/detail-frame';
 import dataForm from '@frame/components/form';
 import dataTable from '@frame/components/filter-page-table';
 import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: trainBatch } = createNamespacedHelpers('trainBatch');
 export default {
   name: 'index',
   props: {},
@@ -39,20 +40,44 @@ export default {
           icon: 'el-icon-edit',
           method: 'edit',
         },
+        {
+          label: '删除',
+          icon: 'el-icon-delete',
+          method: 'delete',
+          confirm: true,
+        },
+        {
+          label: '年度计划管理',
+          icon: 'el-icon-date',
+          method: 'plan',
+        },
       ],
       fields: [
         { label: '标题', prop: 'title' },
-        { label: '状态', prop: 'status' },
+        { label: '年份', prop: 'year' },
+      ],
+      formFields: [
+        { label: '标题', model: 'title', required: true },
+        { label: '年份', model: 'year', required: true },
+        { label: '备注', model: 'remark', required: true, type: 'textarea' },
       ],
-      formFields: [{ label: '标题', model: 'title', required: true }],
       rules: {},
       total: 0,
       form: {},
     };
   },
-  created() {},
+  created() {
+    this.search();
+  },
   methods: {
-    async search({ skip = 0, limit = 10, ...info } = {}) {},
+    ...trainBatch(['query', 'create', 'fetch', 'update', 'delete']),
+    async search({ skip = 0, limit = 10, ...info } = {}) {
+      let res = await this.query({ skip, limit, ...info });
+      if (this.$checkRes(res)) {
+        this.$set(this, `list`, res.data);
+        this.$set(this, `total`, res.total);
+      }
+    },
     toAdd() {
       this.view = 'detail';
     },
@@ -67,9 +92,17 @@ export default {
     async handleSave({ data, isNew }) {
       let duplicate = _.cloneDeep(data);
       let res, msg;
-      if (isNew) (res = await this.create(data)), (msg = '添加');
+      if (!duplicate.id) (res = await this.create(data)), (msg = '添加');
       else (res = await this.update(data)), (msg = '修改');
-      if (this.$checkRes(res, msg, res.errmsg)) this.toBack();
+      if (this.$checkRes(res, `${msg}成功`, res.errmsg)) this.toBack();
+      this.search();
+    },
+    async toDelete({ data }) {
+      let res = await this.delete(data.id);
+      if (this.$checkRes(res, `删除成功`, res.errmsg)) this.search();
+    },
+    toPlan({ data }) {
+      this.$router.push({ path: '/newPlan/index', query: { id: data.id } });
     },
   },
   computed: {