lrf402788946 4 年 前
コミット
f905e54ba7
3 ファイル変更219 行追加16 行削除
  1. 119 0
      src/components/model-card.vue
  2. 99 15
      src/views/statistics/detail.vue
  3. 1 1
      src/views/student/index.vue

+ 119 - 0
src/components/model-card.vue

@@ -0,0 +1,119 @@
+<template>
+  <div id="model-card">
+    <el-card>
+      <template #header>
+        <el-row>
+          <el-col :span="24">选择导出的字段(点击字段可以更换顺序)</el-col>
+          <el-col :span="24">
+            <span v-for="(i, index) in value" :key="`select-${index}`" style="padding-right:15px;zoom:1.1">
+              <el-link type="primary" v-if="index === selectIndex && index !== 0" icon="el-icon-back" @click="toChangePos(i, -1)"></el-link>
+              <el-link @click="toDisplayPos(index)">{{ i }}</el-link>
+              <el-link
+                type="primary"
+                v-if="index === selectIndex && index !== selectModel.length - 1"
+                icon="el-icon-right"
+                @click="toChangePos(i, 1)"
+              ></el-link>
+            </span>
+          </el-col>
+        </el-row>
+      </template>
+      <el-checkbox-group v-model="selectModel">
+        <template v-for="(t, tindex) in getEveryModel()">
+          <el-divider :key="`line-${tindex}`" content-position="left">{{ t.zh }}</el-divider>
+          <el-row :key="`table-${tindex}`">
+            <el-col :span="4" v-for="(i, index) in t.list" :key="`model-${index}`">
+              <el-checkbox :label="i.zh">{{ i.zh }}</el-checkbox>
+            </el-col>
+          </el-row>
+        </template>
+      </el-checkbox-group>
+    </el-card>
+  </div>
+</template>
+
+<script>
+const _ = require('lodash');
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'model-card',
+  model: {
+    prop: 'value',
+    event: 'change',
+  },
+  props: {
+    value: Array,
+    modelList: { type: Array, default: () => [] },
+  },
+  components: {},
+  data: function() {
+    return {
+      selectModel: this.value,
+      selectIndex: '',
+    };
+  },
+  created() {},
+  methods: {
+    // 显示已经选择的字段的移动位置按钮
+    toDisplayPos(index) {
+      this.selectIndex = index;
+    },
+    // 移动位置,
+    toChangePos(data, num) {
+      const dup = _.cloneDeep(this.value);
+      // 当前元素位置
+      const di = dup.findIndex(f => _.isEqual(f, data));
+      // 要交换的元素位置及数据
+      const ci = di + num;
+      const c = dup[ci];
+      // 交换
+      dup[ci] = data;
+      dup[di] = c;
+      this.$set(this, `selectModel`, dup);
+      // 将目标也跟随过去
+      this.toDisplayPos(ci);
+    },
+    getEveryModel() {
+      let dup = _.cloneDeep(this.modelList);
+      dup = _.groupBy(dup, 'table');
+      const arr = [];
+      const keys = Object.keys(dup);
+      for (const key of keys) {
+        let obj = { table: key, list: dup[key] };
+        const h = _.head(dup[key]);
+        if (!h) continue;
+        const { tableZh } = h;
+        if (tableZh) obj.zh = tableZh;
+        arr.push(obj);
+      }
+      return arr;
+    },
+    getModelList(table) {
+      let dup = _.cloneDeep(this.modelList);
+      dup = _.groupBy(dup, 'table');
+    },
+  },
+  watch: {
+    selectModel(val) {
+      this.$emit('change', val);
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.el-checkbox,
+.el-checkbox__input {
+  white-space: normal;
+  word-break: break-all;
+}
+</style>

+ 99 - 15
src/views/statistics/detail.vue

@@ -3,10 +3,17 @@
     <detail-frame :title="pageTitle" :returns="toReturns">
       <el-card>
         <template #header>
-          <span>{{ quest.name }}</span>
-          [<span>完成度:</span><span>{{ questionAnswer.percentage }}%</span> <span>已答:</span><span>{{ questionAnswer.yesAnswer }}</span> <span>未答:</span
-          ><span @click="nostuBtn()" class="nostu">{{ questionAnswer.noAnswer }}</span
-          >]
+          <el-row>
+            <el-col :span="20">
+              <span>{{ quest.name }}</span>
+              [<span>完成度:</span><span>{{ questionAnswer.percentage }}%</span> <span>已答:</span><span>{{ questionAnswer.yesAnswer }}</span>
+              <span>未答:</span><span @click="nostuBtn()" class="nostu">{{ questionAnswer.noAnswer }}</span
+              >]
+            </el-col>
+            <el-col :span="4">
+              <el-button type="primary" size="small" @click="eDialog = true">导出</el-button>
+            </el-col>
+          </el-row>
         </template>
         <el-tabs v-model="tabs" :stretch="true" type="card">
           <el-tab-pane label="图表分析" name="chart">
@@ -42,12 +49,30 @@
         </el-tabs>
       </el-card>
     </detail-frame>
+    <el-dialog title="导出" center :visible.sync="eDialog">
+      <export-range :usestudent="false" @close="eDialog = false" @toExport="toExportExcel">
+        <el-row>
+          <el-col :span="24" style="font-size:16px">
+            表头排列方式:
+            <el-radio-group v-model="direction" class="btn_bar">
+              <el-radio label="horizontal">横向</el-radio>
+              <el-radio label="vertical">纵向</el-radio>
+            </el-radio-group>
+          </el-col>
+          <el-col :span="24">
+            <model-card v-model="selectModel" :modelList="modelList"></model-card>
+          </el-col>
+        </el-row>
+      </export-range>
+    </el-dialog>
   </div>
 </template>
 
 <script>
 //组件全部转移到frame中,和班主任端共用
 import _ from 'lodash';
+import modelCard from '@/components/model-card.vue';
+import exportRange from '@frame/components/export-range';
 import charts from '@frame/parts/statistics/quest-chart.vue';
 import reports from '@frame/parts/statistics/report.vue';
 import detailFrame from '@frame/layout/admin/detail-frame';
@@ -56,13 +81,16 @@ const { mapActions: questionnaire } = createNamespacedHelpers('questionnaire');
 const { mapActions: questionanswer } = createNamespacedHelpers('questionanswer');
 const { mapActions: setting } = createNamespacedHelpers('setting');
 const { mapActions: student } = createNamespacedHelpers('student');
+const { mapActions: util } = createNamespacedHelpers('util');
 
 export default {
   name: 'detail',
   props: {},
-  components: { detailFrame, charts, reports },
+  components: { detailFrame, charts, reports, exportRange, modelCard },
   data: function() {
     return {
+      test: '1',
+      eDialog: false,
       mylb: null,
       tabs: 'chart',
       collapse: '',
@@ -70,23 +98,26 @@ export default {
       cdata: [], //具体数据
       nodata: false,
       questionAnswer: {},
+      // 导出
+      direction: 'horizontal',
+      selectModel: [],
+      modelList: [],
     };
   },
   created() {
     this.search();
+    this.toFindModel();
   },
   methods: {
-    ...questionnaire(['fetch']),
+    ...util(['findModel']),
+    ...questionnaire(['fetch', 'export']),
     ...questionanswer(['query']),
     ...setting({ getSetting: 'fetch' }),
     ...student({ findList: 'findList', studentQuery: 'query' }),
     async search() {
-      console.log(this.querys.questionnaireid);
       let res = await this.fetch(this.querys.questionnaireid);
       if (!this.$checkRes(res)) return;
-      console.log(this.querys);
       let ansres = await this.query(this.querys); //{ questionnaireid: this.id, termid: this.termid, batchid: this.batchid, classid: this.classid }
-      console.log(ansres);
       if (this.$checkRes(ansres)) {
         if (_.get(ansres.data, 'length', 0) <= 0) {
           this.$set(this, `nodata`, true);
@@ -95,7 +126,6 @@ export default {
         let quests = _.cloneDeep(_.get(res.data, `question`, []));
         let allAnswer = _.cloneDeep(ansres.data);
         allAnswer = _.uniqBy(allAnswer, 'studentid');
-        console.log(allAnswer);
         let naa = _.flatten(allAnswer.map(i => i.answers)).map(i => {
           if (i.answer) i.answer = JSON.parse(i.answer);
           return i;
@@ -107,20 +137,17 @@ export default {
         this.$set(this, `quest`, sta);
         //具体数据
         let concreteData = this.getConcreteData(quests, allAnswer);
-        console.log(concreteData);
         this.$set(this, `cdata`, concreteData);
         // 查学生,算已答未答
         const { questionnaireid, ...stuquery } = this.querys;
-        console.log(stuquery);
         let studentList = await this.studentQuery(stuquery);
-        console.log(studentList);
-        console.log(allAnswer);
         let answerDta = {
           yesAnswer: allAnswer.length,
           noAnswer: studentList.total - allAnswer.length,
-          percentage: _.ceil(allAnswer.length / studentList.total, 4) * 100,
+          percentage: _.round(_.ceil(allAnswer.length / studentList.total, 4) * 100, 2),
         };
         this.$set(this, `questionAnswer`, answerDta);
+        this.toSetQuestModelList();
       }
     },
     //获取图表分析部分
@@ -244,6 +271,63 @@ export default {
         query: { termid: this.querys.termid, questionnaireid: this.querys.questionnaireid, classid: this.querys.classid },
       });
     },
+
+    // 找到model
+    async toFindModel() {
+      // 学生字段
+      const res = await this.findModel('student');
+      if (this.$checkRes(res)) {
+        const { data } = res;
+        if (!(data && _.isObject(data))) return;
+        const keys = Object.keys(data);
+        const arr = [];
+        for (const key of keys) {
+          const o = data[key];
+          const { zh } = o;
+          if (zh) {
+            const ao = { zh };
+            ao['model'] = key;
+            ao['table'] = 'student';
+            ao['tableZh'] = '学生信息';
+            arr.push(ao);
+          }
+        }
+        this.$set(this, `modelList`, [...this.modelList, ...arr]);
+      }
+    },
+    // 找到题目
+    toSetQuestModelList() {
+      const { question } = this.quest;
+      if (!(question && _.isArray(question))) return;
+      const arr = [];
+      for (const quest of question) {
+        const { topic, _id } = quest;
+        if (topic) {
+          arr.push({ zh: topic, table: 'questionnaire', _id, tableZh: '问卷题目' });
+        }
+      }
+      this.$set(this, `modelList`, [...this.modelList, ...arr]);
+    },
+    // 导出
+    async toExportExcel(range) {
+      // 数据有: range=>要导出问卷的学生的范围;
+      // direction=>导出表头的方向;
+      // questionnaireid=>要导出的问卷id;
+      // selectModel=>选择的字段,不过需要特殊处理下,因为选择完是中文,需要将对应的object找出来,传给服务端,这样便于服务端聚合数据
+      const direction = _.cloneDeep(this.direction);
+      const questionnaireid = _.cloneDeep(this.querys.questionnaireid);
+      let sml = _.cloneDeep(this.selectModel);
+      const modelList = [];
+      for (const zh of sml) {
+        const res = this.modelList.find(f => _.isEqual(zh, f.zh));
+        if (res) modelList.push(res);
+      }
+      const data = { range, direction, questionnaireid, modelList };
+      const res = await this.export(data);
+      if (this.$checkRes(res)) {
+        window.open(res.data);
+      }
+    },
   },
   computed: {
     ...mapState(['user']),

+ 1 - 1
src/views/student/index.vue

@@ -161,7 +161,7 @@ export default {
   },
   computed: { ...mapState(['user', 'defaultOption']) },
   methods: {
-    ...util(['findModel', 'exportExcel']),
+    ...util(['findModel']),
     ...trainplan({ planfetch: 'fetch' }),
     ...mapActions(['query', 'delete', 'update', 'removeAll', 'computedIsFine', 'toExport']),
     ...classes({ classesquery: 'query' }),