Browse Source

代码合并

zhangqiOMG 2 years ago
parent
commit
3e22572bd6

+ 195 - 1
package-lock.json

@@ -17,11 +17,14 @@
         "core-js": "^3.8.3",
         "countup.js": "^2.0.8",
         "element-ui": "^2.15.6",
+        "file-saver": "^2.0.5",
         "js-md5": "^0.7.3",
+        "script-loader": "^0.7.2",
         "vue": "^2.6.14",
         "vue-countupjs": "^1.0.0",
         "vue-router": "^3.5.1",
-        "vuex": "^3.6.2"
+        "vuex": "^3.6.2",
+        "xlsx": "^0.18.5"
       },
       "devDependencies": {
         "@babel/core": "^7.12.16",
@@ -3445,6 +3448,14 @@
         "node": ">= 0.12.0"
       }
     },
+    "node_modules/adler-32": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
+      "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/aggregate-error": {
       "version": "3.1.0",
       "resolved": "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-3.1.0.tgz",
@@ -4114,6 +4125,18 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/cfb": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
+      "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
+      "dependencies": {
+        "adler-32": "~1.3.0",
+        "crc-32": "~1.2.0"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/chalk": {
       "version": "2.4.2",
       "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
@@ -4369,6 +4392,14 @@
         "node": ">=6"
       }
     },
+    "node_modules/codepage": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
+      "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/color-convert": {
       "version": "1.9.3",
       "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
@@ -4635,6 +4666,17 @@
       "resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.0.8.tgz",
       "integrity": "sha512-pW3xwwD+hB+xmtI16xFcuLS0D5hSQqPQWkZOdgpKQyzxCquDNo2VCFPkRw12vmvdpnicXVTcjmYiakG6biwINg=="
     },
+    "node_modules/crc-32": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+      "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+      "bin": {
+        "crc32": "bin/crc32.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/cross-spawn": {
       "version": "7.0.3",
       "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -6541,6 +6583,11 @@
         "node": "^10.12.0 || >=12.0.0"
       }
     },
+    "node_modules/file-saver": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
+      "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+    },
     "node_modules/fill-range": {
       "version": "7.0.1",
       "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz",
@@ -6680,6 +6727,14 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/frac": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
+      "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/fraction.js": {
       "version": "4.1.3",
       "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.1.3.tgz",
@@ -9951,6 +10006,11 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/raw-loader": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
+      "integrity": "sha512-sf7oGoLuaYAScB4VGr0tzetsYlS8EJH6qnTCfQ/WVEa89hALQ4RQfCKt5xCyPQKPDUbVUAIP1QsxAwfAjlDp7Q=="
+    },
     "node_modules/read-pkg": {
       "version": "5.2.0",
       "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-5.2.0.tgz",
@@ -10378,6 +10438,14 @@
         "node": ">= 8.9.0"
       }
     },
+    "node_modules/script-loader": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/script-loader/-/script-loader-0.7.2.tgz",
+      "integrity": "sha512-UMNLEvgOAQuzK8ji8qIscM3GIrRCWN6MmMXGD4SD5l6cSycgGsCo0tX5xRnfQcoghqct0tjHjcykgI1PyBE2aA==",
+      "dependencies": {
+        "raw-loader": "~0.5.1"
+      }
+    },
     "node_modules/scroll-into-view-if-needed": {
       "version": "2.2.29",
       "resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz",
@@ -10835,6 +10903,17 @@
       "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
       "dev": true
     },
+    "node_modules/ssf": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
+      "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
+      "dependencies": {
+        "frac": "~1.1.2"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/ssr-window": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/ssr-window/-/ssr-window-3.0.0.tgz",
@@ -12382,6 +12461,22 @@
         "node": ">= 0.8.0"
       }
     },
+    "node_modules/wmf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
+      "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/word": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
+      "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/word-wrap": {
       "version": "1.2.3",
       "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.3.tgz",
@@ -12469,6 +12564,26 @@
         }
       }
     },
+    "node_modules/xlsx": {
+      "version": "0.18.5",
+      "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
+      "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
+      "dependencies": {
+        "adler-32": "~1.3.0",
+        "cfb": "~1.2.1",
+        "codepage": "~1.15.0",
+        "crc-32": "~1.2.1",
+        "ssf": "~0.11.2",
+        "wmf": "~1.0.1",
+        "word": "~0.3.0"
+      },
+      "bin": {
+        "xlsx": "bin/xlsx.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/y18n": {
       "version": "5.0.8",
       "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
@@ -15266,6 +15381,11 @@
       "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==",
       "dev": true
     },
+    "adler-32": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
+      "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A=="
+    },
     "aggregate-error": {
       "version": "3.1.0",
       "resolved": "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-3.1.0.tgz",
@@ -15819,6 +15939,15 @@
         "lazy-cache": "^1.0.3"
       }
     },
+    "cfb": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
+      "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
+      "requires": {
+        "adler-32": "~1.3.0",
+        "crc-32": "~1.2.0"
+      }
+    },
     "chalk": {
       "version": "2.4.2",
       "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
@@ -16017,6 +16146,11 @@
         "shallow-clone": "^3.0.0"
       }
     },
+    "codepage": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
+      "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA=="
+    },
     "color-convert": {
       "version": "1.9.3",
       "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
@@ -16251,6 +16385,11 @@
       "resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.0.8.tgz",
       "integrity": "sha512-pW3xwwD+hB+xmtI16xFcuLS0D5hSQqPQWkZOdgpKQyzxCquDNo2VCFPkRw12vmvdpnicXVTcjmYiakG6biwINg=="
     },
+    "crc-32": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+      "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="
+    },
     "cross-spawn": {
       "version": "7.0.3",
       "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -17800,6 +17939,11 @@
         "flat-cache": "^3.0.4"
       }
     },
+    "file-saver": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
+      "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+    },
     "fill-range": {
       "version": "7.0.1",
       "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz",
@@ -17915,6 +18059,11 @@
       "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
       "dev": true
     },
+    "frac": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
+      "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA=="
+    },
     "fraction.js": {
       "version": "4.1.3",
       "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.1.3.tgz",
@@ -20448,6 +20597,11 @@
         }
       }
     },
+    "raw-loader": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
+      "integrity": "sha512-sf7oGoLuaYAScB4VGr0tzetsYlS8EJH6qnTCfQ/WVEa89hALQ4RQfCKt5xCyPQKPDUbVUAIP1QsxAwfAjlDp7Q=="
+    },
     "read-pkg": {
       "version": "5.2.0",
       "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-5.2.0.tgz",
@@ -20790,6 +20944,14 @@
         "ajv-keywords": "^3.5.2"
       }
     },
+    "script-loader": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/script-loader/-/script-loader-0.7.2.tgz",
+      "integrity": "sha512-UMNLEvgOAQuzK8ji8qIscM3GIrRCWN6MmMXGD4SD5l6cSycgGsCo0tX5xRnfQcoghqct0tjHjcykgI1PyBE2aA==",
+      "requires": {
+        "raw-loader": "~0.5.1"
+      }
+    },
     "scroll-into-view-if-needed": {
       "version": "2.2.29",
       "resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz",
@@ -21195,6 +21357,14 @@
       "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
       "dev": true
     },
+    "ssf": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
+      "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
+      "requires": {
+        "frac": "~1.1.2"
+      }
+    },
     "ssr-window": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/ssr-window/-/ssr-window-3.0.0.tgz",
@@ -22430,6 +22600,16 @@
       "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
       "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0="
     },
+    "wmf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
+      "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw=="
+    },
+    "word": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
+      "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA=="
+    },
     "word-wrap": {
       "version": "1.2.3",
       "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.3.tgz",
@@ -22490,6 +22670,20 @@
       "dev": true,
       "requires": {}
     },
+    "xlsx": {
+      "version": "0.18.5",
+      "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
+      "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
+      "requires": {
+        "adler-32": "~1.3.0",
+        "cfb": "~1.2.1",
+        "codepage": "~1.15.0",
+        "crc-32": "~1.2.1",
+        "ssf": "~0.11.2",
+        "wmf": "~1.0.1",
+        "word": "~0.3.0"
+      }
+    },
     "y18n": {
       "version": "5.0.8",
       "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",

+ 4 - 1
package.json

@@ -17,11 +17,14 @@
     "core-js": "^3.8.3",
     "countup.js": "^2.0.8",
     "element-ui": "^2.15.6",
+    "file-saver": "^2.0.5",
     "js-md5": "^0.7.3",
+    "script-loader": "^0.7.2",
     "vue": "^2.6.14",
     "vue-countupjs": "^1.0.0",
     "vue-router": "^3.5.1",
-    "vuex": "^3.6.2"
+    "vuex": "^3.6.2",
+    "xlsx": "^0.18.5"
   },
   "devDependencies": {
     "@babel/core": "^7.12.16",

+ 1 - 1
src/HManagement/archives/index.vue

@@ -3,7 +3,7 @@
     <!-- 课程列表 -->
     <list ref="list">
       <!-- 新增课程 -->
-      <el-button slot="add" icon="el-icon-plus" type="primary" size="small" @click="$router.push({path:'/archives_add'})">新 增</el-button>
+      <el-button slot="add" icon="el-icon-plus" type="primary" size="small" @click="$router.push({path:'/archives_scadd'})">新 增</el-button>
       <el-button slot="detail" type="text" size="small">详 情</el-button>
       <el-button slot="edit" type="text" size="small" class="inline-16">编 辑</el-button>
       <template v-slot:release="scope">

+ 58 - 0
src/HManagement/archives_sc/dataAnalysis/index.vue

@@ -0,0 +1,58 @@
+<template>
+  <div>
+    <pieStudy ref="studyPie" :pieStudyData="pieStudyData"></pieStudy>
+    <tableUnstudy></tableUnstudy>
+    <tableStudy></tableStudy>
+    <pieDownload ref="downloadPie"></pieDownload>
+    <tableUndownload></tableUndownload>
+    <tableDownload></tableDownload>
+  </div>
+</template>
+
+<script>
+import pieStudy from './modules/pieStudy.vue'
+import tableUnstudy from './modules/tableUnstudy.vue'
+import tableStudy from './modules/tableStudy.vue'
+
+import pieDownload from './modules/pieDownload.vue'
+import tableUndownload from './modules/tableUndownload.vue'
+import tableDownload from './modules/tableDownload.vue'
+
+export default {
+  components:{
+    pieStudy,
+    tableUnstudy,
+    tableStudy,
+    pieDownload,
+    tableUndownload,
+    tableDownload
+  },
+  data () {
+    return {
+      pieStudyData:[],
+      pieDownloadData:[]
+    }
+  },
+  methods:{
+    async getAgentData () {
+      const res = await this.$api.requested({
+        "classname": "webmanage.saletool.courseware.statistics.agent",
+        "method": "getAgentStudyData",
+        "content": {
+            "sat_coursewareid": this.$route.query.id
+        }
+      })
+      this.pieStudyData = [{type:'已学习',num:res.data.studyNum},{type:'未学习',num:res.data.unStudyNum}]
+      this.pieDownloadData = [{type:'已下载',num:res.data.downloadNum},{type:'未下载',num:res.data.unDownloadNum}]
+      this.$refs.studyPie.piePlot.changeData(this.pieStudyData)
+      this.$refs.downloadPie.piePlot.changeData(this.pieDownloadData)
+    }
+  },
+  created () {
+    this.getAgentData()
+  }
+}
+
+</script>
+<style>
+</style>

+ 55 - 0
src/HManagement/archives_sc/dataAnalysis/modules/pieDownload.vue

@@ -0,0 +1,55 @@
+<template>
+  <div class="container normal-panel">
+    <p class="normal-margin normal-title">下载数据</p>
+    <div style="width:800px;margin: 0 auto" id="containerDownload"></div>
+  </div>
+</template>
+
+<style>
+/**
+ * 默认尺寸为 600px×400px,如果想让图表响应尺寸变化,可以像下面这样
+ * 把尺寸设为百分比值(同时请记得为容器设置尺寸)。
+ */
+</style>
+
+<script>
+// import * as G2 from '@antv/g2';  
+import { Pie } from '@antv/g2plot';
+export default {
+  props:['pieDownloadData'],
+  data () {
+    return {
+      piePlot () {}
+    }
+  },
+  methods:{
+    renderBar () {
+      this.piePlot = new Pie('containerDownload', {
+        appendPadding: 10,
+        data:[],
+        angleField: 'num',
+        colorField: 'type',
+        color: ['#38C2F6', '#5D76E4'],
+        legend: {
+          position: 'leftTop'
+        },
+        radius: 0.9,
+        label: {
+          type: 'spider',
+          labelHeight: 28,
+          content: '{name}\n{percentage}',
+          style: {
+            fontSize: 14,
+            textAlign: 'center',
+          },
+        },
+        interactions: [{ type: 'element-active' }],
+      });
+      this.piePlot.render();
+    }
+  },
+  mounted() {
+    this.renderBar()
+  }
+}
+</script>

+ 55 - 0
src/HManagement/archives_sc/dataAnalysis/modules/pieStudy.vue

@@ -0,0 +1,55 @@
+<template>
+  <div class="container normal-panel">
+    <p class="normal-margin normal-title">学习数据</p>
+    <div style="width:800px;margin: 0 auto" id="containerStudy"></div>
+  </div>
+</template>
+
+<style>
+/**
+ * 默认尺寸为 600px×400px,如果想让图表响应尺寸变化,可以像下面这样
+ * 把尺寸设为百分比值(同时请记得为容器设置尺寸)。
+ */
+</style>
+
+<script>
+// import * as G2 from '@antv/g2';  
+import { Pie } from '@antv/g2plot';
+export default {
+  props:['pieStudyData'],
+  data () {
+    return {
+      piePlot () {}
+    }
+  },
+  methods:{
+    renderBar () {
+      this.piePlot = new Pie('containerStudy', {
+        appendPadding: 10,
+        data:[],
+        angleField: 'num',
+        colorField: 'type',
+        color: ['#FBB33B', '#F77655'],
+        legend: {
+          position: 'leftTop'
+        },
+        radius: 0.9,
+        label: {
+          type: 'spider',
+          labelHeight: 28,
+          content: '{name}\n{percentage}',
+          style: {
+            fontSize: 14,
+            textAlign: 'center',
+          },
+        },
+        interactions: [{ type: 'element-active' }],
+      });
+      this.piePlot.render();
+    }
+  },
+  mounted() {
+    this.renderBar()
+  }
+}
+</script>

+ 103 - 0
src/HManagement/archives_sc/dataAnalysis/modules/tableDownload.vue

@@ -0,0 +1,103 @@
+<template>
+  <div class="container normal-panel normal-margin">
+    <div class="flex-align-center flex-between normal-margin">
+      <p class="title">已下载经销商</p>
+      <excel :tablecols="tablecols" :param="params" excelTitle="已下载经销商"></excel>
+    </div>
+    <tableLayout :layout="tablecols" :data="list" :custom="true" :height="310">
+      <template v-slot:customcol="scope">
+        <div v-if="scope.column.columnname === 'province'">
+         {{scope.column.data.province}}-{{scope.column.data.city}}-{{scope.column.data.county}}
+        </div>
+        <p v-else>{{scope.column.data[scope.column.columnname]?scope.column.data[scope.column.columnname]:'--'}}</p>
+      </template>
+      <template v-slot:opreation="scope">
+        <slot name="detail" :data="scope"></slot>
+        <slot name="edit" :data="scope"></slot>
+        <slot name="release" :data="scope"></slot>
+        <slot name="data_statistics" :data="scope"></slot>
+        <slot name="del" :data="scope"></slot>
+      </template>
+    </tableLayout>
+    <div style="margin-top:16px;text-align:right">
+      <el-pagination
+        background
+        small
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+        :current-page="currentPage"
+        :page-size="params.content.pageSize"
+        layout="total, prev, pager, next, jumper"
+        :total="total">
+      </el-pagination>
+    </div>
+  </div>
+</template>
+
+<script>
+import excel from '@/components/export_excel/index.vue'
+
+export default {
+  components:{
+    excel
+  },
+  data () {
+    return {
+      params:{
+        "classname": "webmanage.saletool.courseware.statistics.agent",
+        "method": "getDownloadList",
+        "content": {
+          "sat_coursewareid": this.$route.query.id,
+          "pageNumber": 1,
+          "pageSize": 10,
+          "isAll":false
+        }
+      },
+      tablecols:[],
+      list:[],
+      total:0,
+      currentPage:0
+    }
+  },
+  methods:{
+    async getDownloadList () {
+      const res = await this.$api.requested(this.params)
+      this.list = res.data
+      this.total = res.total
+      this.currentPage = res.pageNumber
+    },
+    handleSizeChange(val) {
+      // console.log(`每页 ${val} 条`);
+      this.params.content.pageSize = val
+      this.getDownloadList()
+    },
+    handleCurrentChange(val) {
+      // console.log(`当前页: ${val}`);
+      this.params.content.pageNumber = val
+      this.getDownloadList()
+    },
+  },
+  mounted () {
+    this.getDownloadList()
+  },
+  created () {
+    this.tablecols = this.tool.tabelCol(this.$route.name)['tableDownload'].tablecols
+  }
+}
+
+</script>
+<style>
+
+</style>
+<style scoped>
+.title{
+  height: 20px;
+  line-height: 20px;
+  font-size: 14px;
+  text-indent: 7px;
+  font-weight: bold;
+  color: #333333;
+  margin-bottom: 20px;
+  border-left: 4px solid #3874F6;
+}
+</style>

+ 106 - 0
src/HManagement/archives_sc/dataAnalysis/modules/tableStudy.vue

@@ -0,0 +1,106 @@
+<template>
+  <div class="container normal-panel normal-margin">
+    <div class="flex-align-center flex-between normal-margin">
+      <p class="title">已学习经销商</p>
+      <excel :tablecols="tablecols" :param="params" excelTitle="已学习经销商"></excel>
+    </div>
+    <tableLayout :layout="tablecols" :data="list" :custom="true" :height="310">
+      <template v-slot:customcol="scope">
+        <div v-if="scope.column.columnname === 'province'">
+         {{scope.column.data.province}}-{{scope.column.data.city}}-{{scope.column.data.county}}
+        </div>
+        <div v-else-if="scope.column.columnname === 'isdownloadfile'">
+         {{scope.column.data.isdownloadfile === 1?'是':'否'}}
+        </div>
+        <p v-else>{{scope.column.data[scope.column.columnname]?scope.column.data[scope.column.columnname]:'--'}}</p>
+      </template>
+      <template v-slot:opreation="scope">
+        <slot name="detail" :data="scope"></slot>
+        <slot name="edit" :data="scope"></slot>
+        <slot name="release" :data="scope"></slot>
+        <slot name="data_statistics" :data="scope"></slot>
+        <!-- <el-button size="mini" type="text" @click="onEdit(scope)">数据统计</el-button> -->
+        <slot name="del" :data="scope"></slot>
+      </template>
+    </tableLayout>
+    <div style="margin-top:16px;text-align:right">
+      <el-pagination
+        background
+        small
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+        :current-page="currentPage"
+        :page-size="params.content.pageSize"
+        layout="total, prev, pager, next, jumper"
+        :total="total">
+      </el-pagination>
+    </div>
+  </div>
+</template>
+
+<script>
+import excel from '@/components/export_excel/index.vue'
+export default {
+  components:{
+    excel
+  },
+  data () {
+    return {
+      params:{
+        "classname": "webmanage.saletool.courseware.statistics.agent",
+        "method": "getstudyList",
+        "content": {
+          "sat_coursewareid": this.$route.query.id,
+          "pageNumber": 1,
+          "pageSize": 10,
+          "isAll":false
+        }
+      },
+      tablecols:[],
+      list:[],
+      total:0,
+      currentPage:0
+    }
+  },
+  methods:{
+    async getStudyList () {
+      const res = await this.$api.requested(this.params)
+      this.list = res.data
+      this.total = res.total
+      this.currentPage = res.pageNumber
+    },
+    handleSizeChange(val) {
+      // console.log(`每页 ${val} 条`);
+      this.params.content.pageSize = val
+      this.getStudyList()
+    },
+    handleCurrentChange(val) {
+      // console.log(`当前页: ${val}`);
+      this.params.content.pageNumber = val
+      this.getStudyList()
+    },
+  },
+  mounted () {
+    this.getStudyList()
+  },
+  created () {
+    this.tablecols = this.tool.tabelCol(this.$route.name)['tableStudy'].tablecols
+  }
+}
+
+</script>
+<style>
+
+</style>
+<style scoped>
+.title{
+  height: 20px;
+  line-height: 20px;
+  font-size: 14px;
+  text-indent: 7px;
+  font-weight: bold;
+  color: #333333;
+  margin-bottom: 20px;
+  border-left: 4px solid #3874F6;
+}
+</style>

+ 104 - 0
src/HManagement/archives_sc/dataAnalysis/modules/tableUndownload.vue

@@ -0,0 +1,104 @@
+<template>
+  <div class="container normal-panel">
+    <div class="flex-align-center flex-between normal-margin">
+      <p class="title">未下载经销商</p>
+      <excel :tablecols="tablecols" :param="params" excelTitle="未下载经销商"></excel>
+    </div>
+    <tableLayout :layout="tablecols" :data="list" :custom="true" :height="310">
+      <template v-slot:customcol="scope">
+        <div v-if="scope.column.columnname === 'province'">
+         {{scope.column.data.province}}-{{scope.column.data.city}}-{{scope.column.data.county}}
+        </div>
+        <p v-else>{{scope.column.data[scope.column.columnname]?scope.column.data[scope.column.columnname]:'--'}}</p>
+      </template>
+      <template v-slot:opreation="scope">
+        <slot name="detail" :data="scope"></slot>
+        <slot name="edit" :data="scope"></slot>
+        <slot name="release" :data="scope"></slot>
+        <slot name="data_statistics" :data="scope"></slot>
+        <!-- <el-button size="mini" type="text" @click="onEdit(scope)">数据统计</el-button> -->
+        <slot name="del" :data="scope"></slot>
+      </template>
+    </tableLayout>
+    <div style="margin-top:16px;text-align:right">
+      <el-pagination
+        background
+        small
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+        :current-page="currentPage"
+        :page-size="params.content.pageSize"
+        layout="total, prev, pager, next, jumper"
+        :total="total">
+      </el-pagination>
+    </div>
+  </div>
+</template>
+
+<script>
+import excel from '@/components/export_excel/index.vue'
+
+export default {
+  components:{
+    excel
+  },
+  data () {
+    return {
+      params:{
+        "classname": "webmanage.saletool.courseware.statistics.agent",
+        "method": "getUnDownloadList",
+        "content": {
+          "sat_coursewareid": this.$route.query.id,
+          "pageNumber": 1,
+          "pageSize": 10,
+          "isAll":false
+        }
+      },
+      tablecols:[],
+      list:[],
+      total:0,
+      currentPage:0
+    }
+  },
+  methods:{
+    async getUnDownloadList () {
+      const res = await this.$api.requested(this.params)
+      this.list = res.data
+      this.total = res.total
+      this.currentPage = res.pageNumber
+    },
+    handleSizeChange(val) {
+      // console.log(`每页 ${val} 条`);
+      this.params.content.pageSize = val
+      this.getUnDownloadList()
+    },
+    handleCurrentChange(val) {
+      // console.log(`当前页: ${val}`);
+      this.params.content.pageNumber = val
+      this.getUnDownloadList()
+    },
+  },
+  mounted () {
+    this.getUnDownloadList()
+  },
+  created () {
+    this.tablecols = this.tool.tabelCol(this.$route.name)['tableUndownload'].tablecols
+  }
+}
+
+</script>
+<style>
+
+</style>
+<style scoped>
+.title{
+  height: 20px;
+  line-height: 20px;
+  font-size: 14px;
+  text-indent: 7px;
+  font-weight: bold;
+  color: #333333;
+  margin-bottom: 20px;
+  border-left: 4px solid #3874F6;
+}
+</style>

+ 103 - 0
src/HManagement/archives_sc/dataAnalysis/modules/tableUnstudy.vue

@@ -0,0 +1,103 @@
+<template>
+  <div class="container normal-panel">
+    <div class="flex-align-center flex-between normal-margin">
+      <p class="title">未学习经销商</p>
+      <excel :tablecols="tablecols" :param="params" excelTitle="未学习经销商"></excel>
+    </div>
+    <tableLayout :layout="tablecols" :data="list" :custom="true" :height="310">
+      <template v-slot:customcol="scope">
+        <div v-if="scope.column.columnname === 'province'">
+         {{scope.column.data.province}}-{{scope.column.data.city}}-{{scope.column.data.county}}
+        </div>
+        <p v-else>{{scope.column.data[scope.column.columnname]?scope.column.data[scope.column.columnname]:'--'}}</p>
+      </template>
+      <template v-slot:opreation="scope">
+        <slot name="detail" :data="scope"></slot>
+        <slot name="edit" :data="scope"></slot>
+        <slot name="release" :data="scope"></slot>
+        <slot name="data_statistics" :data="scope"></slot>
+        <!-- <el-button size="mini" type="text" @click="onEdit(scope)">数据统计</el-button> -->
+        <slot name="del" :data="scope"></slot>
+      </template>
+    </tableLayout>
+    <div style="margin-top:16px;text-align:right">
+      <el-pagination
+        background
+        small
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+        :current-page="currentPage"
+        :page-size="params.content.pageSize"
+        layout="total, prev, pager, next, jumper"
+        :total="total">
+      </el-pagination>
+    </div>
+  </div>
+</template>
+
+<script>
+import excel from '@/components/export_excel/index.vue'
+export default {
+  components:{
+    excel
+  },
+  data () {
+    return {
+      params:{
+        "classname": "webmanage.saletool.courseware.statistics.agent",
+        "method": "getUnStudyList",
+        "content": {
+          "sat_coursewareid": this.$route.query.id,
+          "pageNumber": 1,
+          "pageSize": 10,
+          "isAll":false
+        }
+      },
+      tablecols:[],
+      list:[],
+      tableData:[], //用于导出的数据
+      total:0,
+      currentPage:0
+    }
+  },
+  methods:{
+    async getUnStudyList () {
+      const res = await this.$api.requested(this.params)
+      this.list = res.data
+      this.total = res.total
+      this.currentPage = res.pageNumber
+    },
+    handleSizeChange(val) {
+      // console.log(`每页 ${val} 条`);
+      this.params.content.pageSize = val
+      this.getUnStudyList()
+    },
+    handleCurrentChange(val) {
+      // console.log(`当前页: ${val}`);
+      this.params.content.pageNumber = val
+      this.getUnStudyList()
+    }
+  },
+  mounted () {
+    this.getUnStudyList()
+  },
+  created () {
+    this.tablecols = this.tool.tabelCol(this.$route.name)['tableUnstudy'].tablecols
+  }
+}
+
+</script>
+<style>
+
+</style>
+<style scoped>
+.title{
+  height: 20px;
+  line-height: 20px;
+  font-size: 14px;
+  text-indent: 7px;
+  font-weight: bold;
+  color: #333333;
+  border-left: 4px solid #3874F6;
+}
+</style>

+ 7 - 3
src/HManagement/archives_sc/index.vue

@@ -3,13 +3,17 @@
     <!-- 课程列表 -->
     <list ref="list">
       <!-- 新增课程 -->
-      <el-button slot="add" icon="el-icon-plus" type="primary" size="small" @click="$router.push({path:'/archives_add'})">新 增</el-button>
+      <el-button slot="add" icon="el-icon-plus" type="primary" size="small" @click="$router.push({path:'/archives_scadd'})">新 增</el-button>
       <el-button slot="detail" type="text" size="small">详 情</el-button>
-      <el-button slot="edit" type="text" size="small" class="inline-16">编 辑</el-button>
+      <template v-slot:edit="scope">
+        <el-button slot="edit" type="text" size="small" class="inline-16" @click="$router.push({path:'/archives_scedit',query:{id:scope.data.data.sat_coursewareid}})">编 辑</el-button>
+      </template>
       <template v-slot:release="scope">
         <release :data="scope.data.data" @onSuccess="onSuccess"></release>
       </template>
-      <el-button slot="data_statistics" type="text" size="small">数据分析</el-button>
+      <template v-slot:data_statistics="scope">
+        <el-button type="text" size="small" @click="$router.push({path:'/archives_sc_analysis',query:{id:scope.data.data.sat_coursewareid}})">数据分析</el-button>
+      </template>
       <template v-slot:del="scope">
         <on-del :data="scope.data.data" @onSuccess="onSuccess"></on-del>
       </template>

+ 1 - 1
src/HManagement/archives_sc/list/list.vue

@@ -24,7 +24,7 @@
     <tableLayout :layout="tablecols" :data="list" :custom="true" :height="tableHieght">
       <template v-slot:customcol="scope">
         <div v-if="scope.column.columnname === 'status'">
-          <span :style="scope.column.data.status === '发布'?'color:#52C41A':''">{{scope.column.data.status}}</span>
+          <span :style="scope.column.data.status === '新建'?'color:#52C41A':''">{{scope.column.data.status}}</span>
         </div>
         <p v-else>{{scope.column.data[scope.column.columnname]}}</p>
       </template>

+ 109 - 27
src/HManagement/archives_sc/list/modules/add.vue

@@ -3,43 +3,125 @@
     <div class="container normal-panel normal-margin">
       <el-button type="warning" size="small" icon="el-icon-s-claim" style="background:#FA8C16" @click="onSubmit">保 存</el-button>
     </div>
-    <el-row :gutter="16">
-      <el-col :span="12">
-        <div class="container normal-panel normal-margin">
-          <p class="normal-title normal-margin" style="line-height:32px">课程设置</p>
-          <el-form :model="form" class="demo-form-inline" label-position="left" label-width="80px" size="small">
-            <el-form-item label="标题">
-              <el-col :span="24">
-                <el-input v-model="form.title" placeholder="请输入标题"></el-input>
-              </el-col>
-            </el-form-item>
-            <el-form-item label="下架时间">
-            </el-form-item>
-            <el-form-item label="活动区域">
-              <el-select v-model="form.region" placeholder="活动区域">
-                <el-option label="区域一" value="shanghai"></el-option>
-                <el-option label="区域二" value="beijing"></el-option>
-              </el-select>
-            </el-form-item>
-            <el-form-item>
-              <el-button type="primary" @click="onSubmit">查询</el-button>
-            </el-form-item>
-          </el-form>
-        </div>
-      </el-col>
-    </el-row>
+    <div style="overflow:hidden">
+      <el-row :gutter="16">
+        <el-col :span="12">
+          <div class="container normal-panel normal-margin">
+            <p class="normal-title normal-margin" style="line-height:32px">课程设置</p>
+            <el-row :gutter="20">
+              <el-form :model="form" size="small" status-icon label-position="left" label-width="100px" class="demo-ruleForm">
+                <el-col :span="24">
+                  <el-form-item label="标题">
+                    <el-input v-model="form.title" placeholder="请输入标题"></el-input>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                <el-form-item label="开始时间">
+                  <el-date-picker
+                    style="width:100%"
+                    v-model="form.begdate"
+                    value-format="yyyy-MM-dd"
+                    type="date"
+                    placeholder="开始日期">
+                  </el-date-picker>                  
+                </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="下架时间">
+                    <el-date-picker
+                      style="width:100%"
+                      v-model="form.enddate"
+                      value-format="yyyy-MM-dd"
+                      type="date"
+                      placeholder="下架日期">
+                    </el-date-picker>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="一级分类">
+                    <el-select style="width:100%;margin-right:16px" size="small" v-model="form.class" clearable>
+                      <el-option v-for="item in options1" :key="item.index" :value="item.sat_courseware_classid" :label="item.classname" @click.native="handelSelectClick(item)"></el-option>
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="二级分类">
+                    <el-select style="width:100%;margin-right:16px" size="small" v-model="form.sat_courseware_classid" clearable>
+                      <el-option v-for="item in options2" :key="item.index" :value="item.sat_courseware_classid" :label="item.classname"></el-option>
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="24">
+                  <el-form-item label="备注">
+                    <el-input v-model="form.title" placeholder="请输入标备注"></el-input>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="24">
+                  <el-form-item label="封面">
+                    <upload btntype="limage" :folderid="folderid" accept=".JPG,.PNG" :bindData="{ownertable:'SAT_COURSEWARE',ownerid:'',usetype:'cover'}"></upload>
+                    <p class="info">注:建议上传图片大小212x118像素,大小不超过2M,格式为JPG/PNG</p>
+                  </el-form-item>
+                </el-col>
+              </el-form>
+            </el-row>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
   </div>
 </template>
 
 <script>
+import upload from '@/components/upload/hw_obs_upload.vue'
 export default {
+  components:{
+    upload
+  },
   data () {
     return {
-      form:{}
+      options1:[],
+      options2:[],
+      folderid:JSON.parse(sessionStorage.getItem('folderid')).appfolderid,
+      form:{
+        "sat_coursewareid": 0,
+        "title": "测试数据2",
+        "sat_courseware_classid": '',
+        "notes": "备注1",
+        "canfiledownload": 1,
+        "begdate": "2022-05-16 15:01:42",
+        "enddate": "2022-05-16 15:01:42",
+        "tag":[]
+      }
     }
   },
   methods:{
-    onSubmit () {}
+    async onSubmit () {
+      const res = await this.$api.requested({
+        "classname": "webmanage.saletool.courseware.courseware",
+        "method": "insertOrUpdate",
+        "content": this.form
+      })
+      res.code === 1?this.$router.replace({path:'/archives_scedit',query:{id:res.data.sat_coursewareid}}):''
+    },
+    // 分类查询
+    async coursewareclass () {
+      const res = await this.$api.requested({
+        "classname": "webmanage.saletool.courseware.coursewareclass",
+        "method": "select",
+        "content": {
+            "parentid": 0
+        }
+      })
+      this.options1 = res.data
+    },
+    handelSelectClick (val) {
+      this.options2 = val.children
+      this.params.content.where.sat_courseware_classid_2 = ''
+    }
+  },
+  mounted () {
+    this.coursewareclass()
+    this.onSubmit()
   }
 }
 

+ 174 - 0
src/HManagement/archives_sc/list/modules/edit.vue

@@ -0,0 +1,174 @@
+<template>
+  <div>
+    <div class="container normal-panel normal-margin">
+      <el-button type="warning" size="small" icon="el-icon-s-claim" style="background:#FA8C16" @click="onSubmit">保 存</el-button>
+    </div>
+    <div style="overflow-x:hidden">
+      <el-row :gutter="16">
+        <el-col :span="12">
+          <!-- 表单 -->
+          <div class="container normal-panel normal-margin">
+            <p class="normal-title normal-margin" style="line-height:32px">课程设置</p>
+            <el-row :gutter="20">
+              <el-form :model="form" size="small" status-icon label-position="left" label-width="100px" class="demo-ruleForm">
+                <el-col :span="24">
+                  <el-form-item label="标题">
+                    <el-input v-model="form.title" placeholder="请输入标题"></el-input>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                <el-form-item label="开始时间">
+                  <el-date-picker
+                    style="width:100%"
+                    v-model="form.begdate"
+                    value-format="yyyy-MM-dd"
+                    type="date"
+                    placeholder="开始日期">
+                  </el-date-picker>                  
+                </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="下架时间">
+                    <el-date-picker
+                      style="width:100%"
+                      v-model="form.enddate"
+                      value-format="yyyy-MM-dd"
+                      type="date"
+                      placeholder="下架日期">
+                    </el-date-picker>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="一级分类">
+                    <el-select style="width:100%;margin-right:16px" size="small" v-model="form.sat_courseware_classid_1" clearable>
+                      <el-option v-for="item in options1" :key="item.index" :value="item.sat_courseware_classid" :label="item.classname" @click.native="handelSelectClick(item)"></el-option>
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="二级分类">
+                    <el-select style="width:100%;margin-right:16px" size="small" v-model="form.sat_courseware_classid_2" clearable>
+                      <el-option v-for="item in options2" :key="item.index" :value="item.sat_courseware_classid" :label="item.classname"></el-option>
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="24">
+                  <el-form-item label="备注">
+                    <el-input v-model="form.notes" placeholder="请输入标备注"></el-input>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="24">
+                  <el-form-item label="封面">
+                    <previewImage v-if="form.cover" style="width:386px" :image="image" :deletebtn="true" @onSuccess="clearCover"></previewImage>
+                    <upload v-else btntype="limage" :folderid="folderid" accept=".JPG,.PNG" :bindData="{ownertable:'SAT_COURSEWARE',ownerid:form.sat_coursewareid,usetype:'cover'}" @onSuccess="onSubmit"></upload>
+                    <p class="info">注:建议上传图片大小212x118像素,大小不超过2M,格式为JPG/PNG</p>
+                  </el-form-item>
+                </el-col>
+              </el-form>
+            </el-row>
+          </div>
+          <!-- 附件列表 -->
+          <div class="container normal-panel">
+            <attachmentList :attinfos="form.attinfos" @onSuccess="selectDetail">
+              <upload slot="upload" :folderid="folderid" :bindData="{ownertable:'SAT_COURSEWARE',ownerid:form.sat_coursewareid,usetype:'default'}" @onSuccess="onSubmit"></upload>
+            </attachmentList>
+          </div>
+        </el-col>
+        <el-col :span="12">
+          <scopeOfauth></scopeOfauth>
+        </el-col>
+      </el-row>
+    </div>
+  </div>
+</template>
+
+<script>
+import upload from '@/components/upload/hw_obs_upload.vue'
+import previewImage from '@/components/previewImage/index.vue'
+import attachmentList from '@/components/attachment_list/index.vue'
+import scopeOfauth from '@/components/scopeOfAuthority/index.vue'
+export default {
+  components:{
+    upload,
+    previewImage,
+    attachmentList,
+    scopeOfauth
+  },
+  data () {
+    return {
+      options1:[],
+      options2:[],
+      folderid:JSON.parse(sessionStorage.getItem('folderid')).appfolderid,
+      form:{},
+      image:{},
+    }
+  },
+  methods:{
+    async onSubmit () {
+      this.form.sat_courseware_classid = this.form.sat_courseware_classid_2
+      const res = await this.$api.requested({
+        "classname": "webmanage.saletool.courseware.courseware",
+        "method": "insertOrUpdate",
+        "content": this.form
+      })
+      this.selectDetail()
+    },
+    async selectDetail () {
+      const res = await this.$api.requested({
+        "classname": "webmanage.saletool.courseware.courseware",
+        "method": "selectDetail",
+        "content": {
+          "sat_coursewareid":this.$route.query.id
+        }
+      })
+      this.form = Object.assign({},this.form,res.data)
+      this.options1.forEach(e=>{
+        e.sat_courseware_classid === res.data.sat_courseware_classid_1?this.options2 = e.children:''
+      })
+      this.queryFileLink()
+    },
+    // 分类查询
+    async coursewareclass () {
+      const res = await this.$api.requested({
+        "classname": "webmanage.saletool.courseware.coursewareclass",
+        "method": "select",
+        "content": {
+            "parentid": 0
+        }
+      })
+      this.options1 = res.data
+    },
+    handelSelectClick (val) {
+      this.options2 = val.children
+      this.form.sat_courseware_classid_2 = ''
+    },
+    // 获取附件信息
+    async queryFileLink () {
+      this.dialogEditVisible = true
+      const res = await this.$api.requested({
+        "classname": "system.attachment.Attachment",
+        "method": "queryFileLink",
+        "content": {
+          "ownertable": 'SAT_COURSEWARE',
+          "ownerid": this.form.sat_coursewareid,
+          "usetype":'cover'//传空返回有所
+        }
+      })
+      res.data[0]?this.image = res.data[0]:this.image = {url:''}
+    },
+    clearCover () {
+      this.image = {}
+    },
+  },
+  mounted () {
+    this.coursewareclass()
+    this.selectDetail()
+  }
+}
+
+</script>
+<style>
+</style>
+<style scoped>
+
+</style>

+ 107 - 0
src/components/attachment_list/index.vue

@@ -0,0 +1,107 @@
+<template>
+  <div>
+    <div class="flex-align-center flex-between normal-margin">
+      <p>附件列表</p>
+      <slot name="upload"></slot>
+    </div>
+    <el-table
+      :header-cell-style="{background:'#EEEEEE',color:'#333'}" 
+      size="mini"
+      border
+      :data="attinfos"
+      height="300px"
+      style="width: 100%">
+      <el-table-column
+        prop="document"
+        label="文件名称">
+        <template slot-scope="scope">
+          <el-input v-if="actid === scope.row.attachmentid" size="mini" v-model="scope.row.document"></el-input>
+          <span v-else>{{scope.row.document}}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="createdate"
+        label="上传时间">
+      </el-table-column>
+      <el-table-column
+        prop="contentlength"
+        label="文件大小"
+        width="90">
+        <template slot-scope="scope">
+          {{scope.row.contentlength > 1073741824?(scope.row.contentlength / Math.pow(1024,3)).toFixed(2)+'GB':scope.row.contentlength > 1048576?(scope.row.contentlength / Math.pow(1024,2)).toFixed(2)+'MB':scope.row.contentlength > 1024?(scope.row.contentlength / Math.pow(1024,1)).toFixed(2)+'KB':scope.row.contentlength+'B'}}
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="操作"
+        width="140">
+        <template slot-scope="scope">
+          <div v-if="actid === scope.row.attachmentid">
+            <el-button type="text" size="small" @click="saveEdit(scope.row)">保 存</el-button>
+            <el-button type="text" size="small" @click="actid = 0">取 消</el-button>
+          </div>
+          <div v-else>
+            <el-button type="text" size="small" @click="download(scope.row)">下 载</el-button>
+            <el-button class="inline-16" type="text" size="small" @click="editAttachment(scope.row)">编 辑</el-button>
+            <el-popconfirm
+              title="确定删除当前附件吗?"
+              @confirm="deleteAttachment(scope.row)">
+              <el-button slot="reference" size="small" type="text">删 除</el-button>
+            </el-popconfirm>
+          </div>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script>
+export default {
+  props:['attinfos'],
+  data () {
+    return {
+      actid:0
+    }
+  },
+  methods:{
+    editAttachment (row) {
+      this.actid = row.attachmentid
+    },
+    download (row) {
+      window.open(row.url)
+    },
+    async saveEdit (row) {
+      let param = {
+        "classname": "system.attachment.MediaCenter",
+        "method": "changeAttachment",
+        "content": {
+          "files": [
+            {
+              "attachmentid": row.attachmentid,
+              "document": row.document,
+              "parentid":row.parentid
+            }
+          ]
+        }
+      }
+      const res = await this.$api.requested(param)
+      res.code === 1?this.$emit('onSuccess'):''
+      res.code === 1?this.actid = '':''
+    },
+    
+    async deleteAttachment (row) {
+      const res = await this.$api.requested({
+        "classname": "system.attachment.MediaCenter",
+        "method": "deleteAttachment",
+        "content": {
+            "attachmentid":row.attachmentid
+        }
+      })
+      res.code === 1?this.tool.showMessage(res):''
+      res.code === 1?this.$emit('onSuccess'):''
+    }
+  }
+}
+
+</script>
+<style>
+</style>

+ 37 - 0
src/components/export_excel/index.vue

@@ -0,0 +1,37 @@
+<template>
+  <div>
+    <el-button type="success" size="small" icon="el-icon-download" @click="submit()">导 出</el-button>
+  </div>
+</template>
+
+<script>
+export default {
+  props:['tablecols','param','excelTitle'],
+  data () {
+    return {}
+  },
+  methods:{
+    async submit () {
+      this.param.content.isAll = true
+      const res = await this.$api.requested(this.param)
+      let table = JSON.parse(JSON.stringify(this.tablecols))
+      table.forEach((e,index) => {
+        if (e.title === '省市县') {
+          table[index].title = '省'
+          table.splice(index + 1,0,{title:'市',columnname:'city'},{title:'县',columnname:'county'})
+        }
+      });
+      let hd = table.map(e=>{
+        return e.title
+      })
+      let ft = table.map(e=>{
+        return e.columnname
+      })
+      this.tool.exportExcel(hd,ft,res.data,this.excelTitle)
+    }
+  }
+}
+
+</script>
+<style>
+</style>

+ 1 - 1
src/components/previewImage/index.vue

@@ -2,7 +2,7 @@
   <div class="image-panel">
     <i v-if="deletebtn" class="el-icon-error close-btn" @click="deleteFileLink"></i>
     <el-image 
-      style="width: 100%; height: 100%"
+      style="width: 100%;"
       :src="image.url" 
       :preview-src-list="srcList">
     </el-image>

+ 88 - 0
src/components/scopeOfAuthority/index.vue

@@ -0,0 +1,88 @@
+<template>
+  <div class="container normal-panel normal-margin" style="line-height:32px">
+    <p class="normal-title normal-margin">发布范围</p>
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <div>
+          <div class="flex-align-center flex-between normal-margin">
+            <el-checkbox v-model="allcheck1">全 选</el-checkbox>
+            <el-checkbox v-model="allcheck1">仅组织负责人可见</el-checkbox>
+          </div>
+          <div class="panel">
+            <div class="tab flex-align-center">
+              <p :class="activeName === '组织架构'?'tab-act':''" @click="tabChange('组织架构')">组织架构</p>
+              <p :class="activeName === '营销组织'?'tab-act':''" @click="tabChange('营销组织')">营销组织</p>
+            </div>
+            <div>
+              <deplist v-if="activeName === '组织架构'" :checked="true"></deplist>
+              <arealist v-else  :checked="true"></arealist>
+            </div>
+          </div>
+        </div>
+      </el-col>
+      <el-col :span="12">
+        <div>
+          <!-- <div class="flex-align-center flex-between normal-margin">
+            <el-checkbox v-model="allcheck1">全 选</el-checkbox>
+            <el-checkbox v-model="allcheck1">仅组织负责人可见</el-checkbox>
+          </div> -->
+          <div></div>
+          <div class="panel">
+            <div class="tab flex-align-center">
+              <p>人员信息</p>
+            </div>
+            <div>
+            </div>
+          </div>
+        </div>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import deplist from '@/components/selectMenber/modules/dep_list.vue'
+import arealist from '@/components/selectAgent/area/list.vue'
+
+export default {
+  components:{
+    deplist,
+    arealist
+  },
+  data () {
+    return {
+      activeName:'组织架构',
+      allcheck1:false,
+    }
+  },
+  methods:{
+    tabChange (val) {
+      this.activeName = val
+    }
+  }
+}
+
+</script>
+<style>
+</style>
+<style scoped>
+.panel{
+  border:1px solid #CCCCCC;
+  border-radius: 4px;
+}
+.tab p{
+  flex:1;
+  height: 39px;
+  line-height: 39px;
+  text-align: center;
+  font-weight: 500;
+  font-size: 14px;
+  color:#999;
+  border-bottom: 2px solid #eeeeee;
+  cursor:pointer;
+}
+.tab-act{
+  color:#3874F6 !important;
+  border-bottom: 2px solid #3874F6 !important;
+}
+</style>

+ 9 - 1
src/components/selectAgent/area/list.vue

@@ -5,7 +5,11 @@
       node-key="id"
       default-expand-all
       highlight-current
+      :show-checkbox="checked"
+      :check-strictly="true"
+      :check-on-click-node="true"
       :expand-on-click-node="false"
+      @check-change="handleCheckChange"
       @node-click="handleClick">
       <span class="custom-tree-node" slot-scope="{ node, data }">
         <span>{{ node.label }}</span>
@@ -21,6 +25,7 @@
 
 
 export default {
+  props:['checked'],
   components:{
   },
   data () {
@@ -71,7 +76,10 @@ export default {
     },
     handleClick (row,node,VueComponent) {
       this.$emit('onClick',node.data)
-    }
+    },
+    handleCheckChange(data, checked, indeterminate) {
+      console.log(data, checked, indeterminate);
+    },
   },
   mounted () {
     this.query_arealist()

+ 27 - 1
src/components/selectMenber/modules/dep_list.vue

@@ -6,6 +6,8 @@
       default-expand-all
       highlight-current
       :expand-on-click-node="false"
+      :show-checkbox="checked"
+      @check-change="handleCheckChange"
       @node-click="handleClick">
       <span class="custom-tree-node" slot-scope="{ node, data }">
         <span>{{ node.label }}</span>
@@ -21,6 +23,7 @@
 
 
 export default {
+  props:['checked'],
   components:{
   },
   data () {
@@ -77,7 +80,30 @@ export default {
     },
     handleClick (row,node,VueComponent) {
       this.$emit('onClick',node)
-    }
+    },
+    handleCheckChange(data, checked, indeterminate) {
+      console.log(data, checked, indeterminate);
+    },
+
+    // 构建赋值
+    setChecked() {
+      console.log('===进⼊设置状态==', this.select)
+      const getCheck = this.select
+      if (getCheck.length > 0) {
+        getCheck.forEach((i, n) => {
+          const node = this.$refs.tree.getNode(i)
+          console.log('===循环==')
+          if (node != null) {
+            if (node.isLeaf) {
+              this.$refs.tree.setChecked(node, true)
+            } else {
+              node.indeterminate = true
+              node.checked = true
+            }
+          }
+        })
+      }
+    },
   },
   mounted () {
     this.department()

+ 0 - 1
src/components/upload/hw_obs_upload.vue

@@ -130,7 +130,6 @@ export default {
       }
       
       const res = await this.$api.requested(param)
-      console.log(res);
       this.$emit('onSuccess')
     },
 

+ 105 - 91
src/router/HManagement.js

@@ -55,97 +55,111 @@ const HManagement = [
       ast_nav:true
     },
     component: () => import(/* webpackChunkName: "about" */ '@/HManagement/department/staff/detail/index.vue')
-  },
-  // },{
-  //   path: '/salermag',
-  //   name: 'salermag',
-  //   meta: {
-  //     title: '营销组织',
-  //     ast_nav:true
-  //   },
-  //   component: () => import(/* webpackChunkName: "about" */ '@/HManagement/marketing/index.vue')
-  // },{
-  //   path: '/add_saler',
-  //   name: 'salermag',
-  //   meta: {
-  //     title: '新建业务员',
-  //     ast_nav:true
-  //   },
-  //   component: () => import(/* webpackChunkName: "about" */ '@/HManagement/marketing/saler/modules/add_saler.vue')
-  // },{
-  //   path: '/saler_detail',
-  //   name: 'salermag',
-  //   meta: {
-  //     title: '业务员详情',
-  //     ast_nav:true
-  //   },
-  //   component: () => import(/* webpackChunkName: "about" */ '@/HManagement/marketing/saler/modules/saler_detail.vue')
-  // },{
-  //   path: '/add_agent',
-  //   name: 'salermag',
-  //   meta: {
-  //     title: '新建经销商',
-  //     ast_nav:true
-  //   },
-  //   component: () => import(/* webpackChunkName: "about" */ '@/HManagement/marketing/agent/modules/add_agent.vue')
-  // },{
-  //   path: '/edit_agent',
-  //   name: 'salermag',
-  //   meta: {
-  //     title: '编辑经销商',
-  //     ast_nav:true
-  //   },
-  //   component: () => import(/* webpackChunkName: "about" */ '@/HManagement/marketing/agent/modules/edit_agent.vue')
-  // },{
-  //   path: '/agent_detail',
-  //   name: 'salermag',
-  //   meta: {
-  //     title: '经销商详情',
-  //     ast_nav:true
-  //   },
-  //   component: () => import(/* webpackChunkName: "about" */ '@/HManagement/marketing/agent/modules/agent_detail.vue')
-  // },{
-  //   path: '/notice_mag_list',
-  //   name: 'noticemag',
-  //   meta: {
-  //     title: '通告管理',
-  //     ast_nav:true
-  //   },
-  //   component: () => import(/* webpackChunkName: "about" */ '@/HManagement/notice/notice_mag/index.vue')
-  // },{
-  //   path: '/archives_list',
-  //   name: 'archives',
-  //   meta: {
-  //     title: ' 营销物料',
-  //     ast_nav:true
-  //   },
-  //   component: () => import(/* webpackChunkName: "about" */ '@/HManagement/archives/index.vue')
-  // },{
-  //   path: '/archives_ad_list',
-  //   name: 'archives_ad',
-  //   meta: {
-  //     title: ' 营销物料',
-  //     ast_nav:true
-  //   },
-  //   component: () => import(/* webpackChunkName: "about" */ '@/HManagement/archives/index.vue')
-  // },{
-  //   path: '/archives_scmag',
-  //   name: 'archives_scmag',
-  //   meta: {
-  //     title: '商学院管理',
-  //     ast_nav:true
-  //   },
-  //   component: () => import(/* webpackChunkName: "about" */ '@/HManagement/archives_sc/index.vue')
-  // },{
-  //   path: '/archives_add',
-  //   name: 'archives_scmag',
-  //   meta: {
-  //     title: '商学院管理',
-  //     ast_nav:true
-  //   },
-  //   component: () => import(/* webpackChunkName: "about" */ '@/HManagement/archives_sc/list/modules/add.vue')
-  // },
-  {
+  },{
+    path: '/salermag',
+    name: 'salermag',
+    meta: {
+      title: '营销组织',
+      ast_nav:true
+    },
+    component: () => import(/* webpackChunkName: "about" */ '@/HManagement/marketing/index.vue')
+  },{
+    path: '/add_saler',
+    name: 'salermag',
+    meta: {
+      title: '新建业务员',
+      ast_nav:true
+    },
+    component: () => import(/* webpackChunkName: "about" */ '@/HManagement/marketing/saler/modules/add_saler.vue')
+  },{
+    path: '/saler_detail',
+    name: 'salermag',
+    meta: {
+      title: '业务员详情',
+      ast_nav:true
+    },
+    component: () => import(/* webpackChunkName: "about" */ '@/HManagement/marketing/saler/modules/saler_detail.vue')
+  },{
+    path: '/add_agent',
+    name: 'salermag',
+    meta: {
+      title: '新建经销商',
+      ast_nav:true
+    },
+    component: () => import(/* webpackChunkName: "about" */ '@/HManagement/marketing/agent/modules/add_agent.vue')
+  },{
+    path: '/edit_agent',
+    name: 'salermag',
+    meta: {
+      title: '编辑经销商',
+      ast_nav:true
+    },
+    component: () => import(/* webpackChunkName: "about" */ '@/HManagement/marketing/agent/modules/edit_agent.vue')
+  },{
+    path: '/agent_detail',
+    name: 'salermag',
+    meta: {
+      title: '经销商详情',
+      ast_nav:true
+    },
+    component: () => import(/* webpackChunkName: "about" */ '@/HManagement/marketing/agent/modules/agent_detail.vue')
+  },{
+    path: '/notice_mag_list',
+    name: 'noticemag',
+    meta: {
+      title: '通告管理',
+      ast_nav:true
+    },
+    component: () => import(/* webpackChunkName: "about" */ '@/HManagement/notice/notice_mag/index.vue')
+  },{
+    path: '/archives_list',
+    name: 'archives',
+    meta: {
+      title: ' 营销物料',
+      ast_nav:true
+    },
+    component: () => import(/* webpackChunkName: "about" */ '@/HManagement/archives/index.vue')
+  },{
+    path: '/archives_ad_list',
+    name: 'archives_ad',
+    meta: {
+      title: ' 营销物料',
+      ast_nav:true
+    },
+    component: () => import(/* webpackChunkName: "about" */ '@/HManagement/archives/index.vue')
+  },{
+    path: '/archives_scmag',
+    name: 'archives_scmag',
+    meta: {
+      title: '商学院管理',
+      ast_nav:true
+    },
+    component: () => import(/* webpackChunkName: "about" */ '@/HManagement/archives_sc/index.vue')
+  },{
+    path: '/archives_scadd',
+    name: 'archives_scmag',
+    meta: {
+      title: '编辑课程',
+      ast_nav:true
+    },
+    component: () => import(/* webpackChunkName: "about" */ '@/HManagement/archives_sc/list/modules/add.vue')
+  },{
+    path: '/archives_scedit',
+    name: 'archives_scmag',
+    meta: {
+      title: '编辑课程',
+      ast_nav:true
+    },
+    component: () => import(/* webpackChunkName: "about" */ '@/HManagement/archives_sc/list/modules/edit.vue')
+  },{
+    path: '/archives_sc_analysis',
+    name: 'archives_scmag',
+    meta: {
+      title: '数据分析',
+      ast_nav:true
+    },
+    component: () => import(/* webpackChunkName: "about" */ '@/HManagement/archives_sc/dataAnalysis/index.vue')
+  },{
     path: '/security_config',
     name: 'companyInformation',
     meta: {

+ 179 - 0
src/utils/excel/Blob.js

@@ -0,0 +1,179 @@
+/* eslint-disable */
+/* Blob.js
+ * A Blob implementation.
+ * 2014-05-27
+ *
+ * By Eli Grey, http://eligrey.com
+ * By Devin Samarin, https://github.com/eboyjr
+ * License: X11/MIT
+ *   See LICENSE.md
+ */
+
+/*global self, unescape */
+/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
+ plusplus: true */
+
+/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
+
+(function (view) {
+    "use strict";
+
+    view.URL = view.URL || view.webkitURL;
+
+    if (view.Blob && view.URL) {
+        try {
+            new Blob;
+            return;
+        } catch (e) {}
+    }
+
+    // Internally we use a BlobBuilder implementation to base Blob off of
+    // in order to support older browsers that only have BlobBuilder
+    var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) {
+            var
+                get_class = function(object) {
+                    return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
+                }
+                , FakeBlobBuilder = function BlobBuilder() {
+                    this.data = [];
+                }
+                , FakeBlob = function Blob(data, type, encoding) {
+                    this.data = data;
+                    this.size = data.length;
+                    this.type = type;
+                    this.encoding = encoding;
+                }
+                , FBB_proto = FakeBlobBuilder.prototype
+                , FB_proto = FakeBlob.prototype
+                , FileReaderSync = view.FileReaderSync
+                , FileException = function(type) {
+                    this.code = this[this.name = type];
+                }
+                , file_ex_codes = (
+                    "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
+                    + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
+                ).split(" ")
+                , file_ex_code = file_ex_codes.length
+                , real_URL = view.URL || view.webkitURL || view
+                , real_create_object_URL = real_URL.createObjectURL
+                , real_revoke_object_URL = real_URL.revokeObjectURL
+                , URL = real_URL
+                , btoa = view.btoa
+                , atob = view.atob
+
+                , ArrayBuffer = view.ArrayBuffer
+                , Uint8Array = view.Uint8Array
+                ;
+            FakeBlob.fake = FB_proto.fake = true;
+            while (file_ex_code--) {
+                FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
+            }
+            if (!real_URL.createObjectURL) {
+                URL = view.URL = {};
+            }
+            URL.createObjectURL = function(blob) {
+                var
+                    type = blob.type
+                    , data_URI_header
+                    ;
+                if (type === null) {
+                    type = "application/octet-stream";
+                }
+                if (blob instanceof FakeBlob) {
+                    data_URI_header = "data:" + type;
+                    if (blob.encoding === "base64") {
+                        return data_URI_header + ";base64," + blob.data;
+                    } else if (blob.encoding === "URI") {
+                        return data_URI_header + "," + decodeURIComponent(blob.data);
+                    } if (btoa) {
+                        return data_URI_header + ";base64," + btoa(blob.data);
+                    } else {
+                        return data_URI_header + "," + encodeURIComponent(blob.data);
+                    }
+                } else if (real_create_object_URL) {
+                    return real_create_object_URL.call(real_URL, blob);
+                }
+            };
+            URL.revokeObjectURL = function(object_URL) {
+                if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
+                    real_revoke_object_URL.call(real_URL, object_URL);
+                }
+            };
+            FBB_proto.append = function(data/*, endings*/) {
+                var bb = this.data;
+                // decode data to a binary string
+                if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
+                    var
+                        str = ""
+                        , buf = new Uint8Array(data)
+                        , i = 0
+                        , buf_len = buf.length
+                        ;
+                    for (; i < buf_len; i++) {
+                        str += String.fromCharCode(buf[i]);
+                    }
+                    bb.push(str);
+                } else if (get_class(data) === "Blob" || get_class(data) === "File") {
+                    if (FileReaderSync) {
+                        var fr = new FileReaderSync;
+                        bb.push(fr.readAsBinaryString(data));
+                    } else {
+                        // async FileReader won't work as BlobBuilder is sync
+                        throw new FileException("NOT_READABLE_ERR");
+                    }
+                } else if (data instanceof FakeBlob) {
+                    if (data.encoding === "base64" && atob) {
+                        bb.push(atob(data.data));
+                    } else if (data.encoding === "URI") {
+                        bb.push(decodeURIComponent(data.data));
+                    } else if (data.encoding === "raw") {
+                        bb.push(data.data);
+                    }
+                } else {
+                    if (typeof data !== "string") {
+                        data += ""; // convert unsupported types to strings
+                    }
+                    // decode UTF-16 to binary string
+                    bb.push(unescape(encodeURIComponent(data)));
+                }
+            };
+            FBB_proto.getBlob = function(type) {
+                if (!arguments.length) {
+                    type = null;
+                }
+                return new FakeBlob(this.data.join(""), type, "raw");
+            };
+            FBB_proto.toString = function() {
+                return "[object BlobBuilder]";
+            };
+            FB_proto.slice = function(start, end, type) {
+                var args = arguments.length;
+                if (args < 3) {
+                    type = null;
+                }
+                return new FakeBlob(
+                    this.data.slice(start, args > 1 ? end : this.data.length)
+                    , type
+                    , this.encoding
+                );
+            };
+            FB_proto.toString = function() {
+                return "[object Blob]";
+            };
+            FB_proto.close = function() {
+                this.size = this.data.length = 0;
+            };
+            return FakeBlobBuilder;
+        }(view));
+
+    view.Blob = function Blob(blobParts, options) {
+        var type = options ? (options.type || "") : "";
+        var builder = new BlobBuilder();
+        if (blobParts) {
+            for (var i = 0, len = blobParts.length; i < len; i++) {
+                builder.append(blobParts[i]);
+            }
+        }
+        return builder.getBlob(type);
+    };
+}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));

+ 141 - 0
src/utils/excel/Export2Excel.js

@@ -0,0 +1,141 @@
+/* eslint-disable */
+require('script-loader!file-saver');
+require('./Blob.js');
+require('script-loader!xlsx/dist/xlsx.core.min');
+function generateArray(table) {
+    var out = [];
+    var rows = table.querySelectorAll('tr');
+    var ranges = [];
+    for (var R = 0; R < rows.length; ++R) {
+        var outRow = [];
+        var row = rows[R];
+        var columns = row.querySelectorAll('td');
+        for (var C = 0; C < columns.length; ++C) {
+            var cell = columns[C];
+            var colspan = cell.getAttribute('colspan');
+            var rowspan = cell.getAttribute('rowspan');
+            var cellValue = cell.innerText;
+            if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
+
+            //Skip ranges
+            ranges.forEach(function (range) {
+                if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
+                    for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
+                }
+            });
+
+            //Handle Row Span
+            if (rowspan || colspan) {
+                rowspan = rowspan || 1;
+                colspan = colspan || 1;
+                ranges.push({s: {r: R, c: outRow.length}, e: {r: R + rowspan - 1, c: outRow.length + colspan - 1}});
+            }
+            ;
+
+            //Handle Value
+            outRow.push(cellValue !== "" ? cellValue : null);
+
+            //Handle Colspan
+            if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
+        }
+        out.push(outRow);
+    }
+    return [out, ranges];
+};
+
+function datenum(v, date1904) {
+    if (date1904) v += 1462;
+    var epoch = Date.parse(v);
+    return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
+}
+
+function sheet_from_array_of_arrays(data, opts) {
+    var ws = {};
+    var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}};
+    for (var R = 0; R != data.length; ++R) {
+        for (var C = 0; C != data[R].length; ++C) {
+            if (range.s.r > R) range.s.r = R;
+            if (range.s.c > C) range.s.c = C;
+            if (range.e.r < R) range.e.r = R;
+            if (range.e.c < C) range.e.c = C;
+            var cell = {v: data[R][C]};
+            if (cell.v == null) continue;
+            var cell_ref = XLSX.utils.encode_cell({c: C, r: R});
+
+            if (typeof cell.v === 'number') cell.t = 'n';
+            else if (typeof cell.v === 'boolean') cell.t = 'b';
+            else if (cell.v instanceof Date) {
+                cell.t = 'n';
+                cell.z = XLSX.SSF._table[14];
+                cell.v = datenum(cell.v);
+            }
+            else cell.t = 's';
+
+            ws[cell_ref] = cell;
+        }
+    }
+    if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
+    return ws;
+}
+
+function Workbook() {
+    if (!(this instanceof Workbook)) return new Workbook();
+    this.SheetNames = [];
+    this.Sheets = {};
+}
+
+function s2ab(s) {
+    var buf = new ArrayBuffer(s.length);
+    var view = new Uint8Array(buf);
+    for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
+    return buf;
+}
+
+export function export_table_to_excel(id) {
+    var theTable = document.getElementById(id);
+    console.log('a')
+    var oo = generateArray(theTable);
+    var ranges = oo[1];
+
+    /* original data */
+    var data = oo[0];
+    var ws_name = "SheetJS";
+    console.log(data);
+
+    var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
+
+    /* add ranges to worksheet */
+    // ws['!cols'] = ['apple', 'banan'];
+    ws['!merges'] = ranges;
+
+    /* add worksheet to workbook */
+    wb.SheetNames.push(ws_name);
+    wb.Sheets[ws_name] = ws;
+
+    var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
+
+    saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx")
+}
+
+function formatJson(jsonData) {
+    console.log(jsonData)
+}
+export function export_json_to_excel(th, jsonData, defaultTitle) {
+
+    /* original data */
+
+    var data = jsonData;
+    data.unshift(th);
+    var ws_name = "SheetJS";
+
+    var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
+
+
+    /* add worksheet to workbook */
+    wb.SheetNames.push(ws_name);
+    wb.Sheets[ws_name] = ws;
+
+    var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
+    var title = defaultTitle || '列表'
+    saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), title + ".xlsx")
+}

+ 15 - 0
src/utils/tool.js

@@ -79,4 +79,19 @@ export default {
     })
     return obj
   },
+
+  // 导出excel
+  exportExcel(a,b,c,d) {
+    let formatJson = function(filterVal, jsonData) {
+      return jsonData.map(v => filterVal.map(j => v[j]))
+    }
+    require.ensure([], () => {
+      const { export_json_to_excel } = require('./excel/Export2Excel');
+      const tHeader = a;  // 设置Excel的表格第一行的标题
+      const filterVal = b;  // index、nickName、name是tableData里对象的属性
+      const list = c;  //把data里的tableData存到list
+      const data = formatJson(filterVal, list);
+      export_json_to_excel(tHeader, data, d);  //导出Excel 文件名
+    })
+  }
 } 

+ 2 - 2
vue.config.js

@@ -6,8 +6,8 @@ module.exports = {
     publicPath: './',
     devServer: {
       open: true,
-      host: '192.168.4.170',
-      // host: 'localhost',
+      // host: '192.168.4.170',
+      host: 'localhost',
       port: 8080,
       proxy: {
         '/apis': {