exerciseDuration.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. <template>
  2. <div class="m-container">
  3. <!-- 搜索标题 -->
  4. <saveform
  5. :inline="true"
  6. ref="searchForm"
  7. class="searchForm"
  8. saveKey="exerciseDuration"
  9. :model.sync="searchForm"
  10. >
  11. <el-form-item prop="dates">
  12. <!-- @change="changeWeek" -->
  13. <el-date-picker
  14. :clearable="false"
  15. v-model="searchForm.dates"
  16. value-format="yyyy-MM-dd"
  17. :picker-options="{ firstDayOfWeek: 1 }"
  18. type="daterange"
  19. style="width: 405px"
  20. range-separator="至"
  21. start-placeholder="开始日期"
  22. end-placeholder="结束日期"
  23. >
  24. </el-date-picker>
  25. </el-form-item>
  26. <el-form-item>
  27. <el-button @click="search" type="danger">搜索</el-button>
  28. <el-button @click="onReSet" type="primary">重置</el-button>
  29. </el-form-item>
  30. </saveform>
  31. <!-- 分组添加柱状图 -->
  32. <ve-histogram
  33. style="width: 100%"
  34. height="350px"
  35. :data="chartData"
  36. :data-empty="dataEmpty"
  37. :extend="chartExtend"
  38. :legend="legend"
  39. :data-zoom="dataZoom"
  40. ></ve-histogram>
  41. <el-button
  42. type="primary"
  43. @click="exportQuestion"
  44. style="margin: 16px 0"
  45. v-permission="'studentCoursewarePlayRecord/exportStatList'"
  46. >导出</el-button
  47. >
  48. <div class="tableWrap">
  49. <el-table
  50. :data="tableList"
  51. :header-cell-style="{ background: '#EDEEF0', color: '#444' }"
  52. @sort-change="onSortChange"
  53. >
  54. <el-table-column width="120px" align="center" prop="id" label="排名">
  55. <template slot-scope="scope">
  56. <div>
  57. {{ scope.$index + 1 }}
  58. </div>
  59. </template>
  60. </el-table-column>
  61. <el-table-column
  62. align="center"
  63. prop="organizationName"
  64. label="分部名称"
  65. ></el-table-column>
  66. <el-table-column
  67. align="center"
  68. prop="memberNum"
  69. sortable="custom"
  70. label="会员人数"
  71. width="100px"
  72. >
  73. <template slot-scope="scope">
  74. <div>{{ scope.row.memberNum }}人</div>
  75. </template>
  76. </el-table-column>
  77. <el-table-column
  78. align="center"
  79. prop="noPlayNum"
  80. sortable="custom"
  81. label="无练习人数"
  82. width="120px"
  83. >
  84. <template slot-scope="scope">
  85. <div>{{ scope.row.noPlayNum }}人</div>
  86. </template>
  87. </el-table-column>
  88. <el-table-column
  89. align="center"
  90. prop="playTimeLess10"
  91. sortable="custom"
  92. label="0-10分钟"
  93. width="120px"
  94. >
  95. <template slot-scope="scope">
  96. <div>{{ scope.row.playTimeLess10 }}人</div>
  97. </template>
  98. </el-table-column>
  99. <el-table-column
  100. align="center"
  101. prop="playTimeLess60"
  102. sortable="custom"
  103. label="10-60分钟"
  104. width="120px"
  105. >
  106. <template slot-scope="scope">
  107. <div>{{ scope.row.playTimeLess60 }}人</div>
  108. </template></el-table-column
  109. >
  110. <el-table-column
  111. align="center"
  112. prop="playTimeLess120"
  113. sortable="custom"
  114. label="60-120分钟"
  115. width="120px"
  116. >
  117. <template slot-scope="scope">
  118. <div>{{ scope.row.playTimeLess120 }}人</div>
  119. </template>
  120. </el-table-column>
  121. <el-table-column
  122. align="center"
  123. prop="playTimeLess240"
  124. sortable="custom"
  125. label="120-240分钟"
  126. width="130px"
  127. >
  128. <template slot-scope="scope">
  129. <div>{{ scope.row.playTimeLess240 }}人</div>
  130. </template>
  131. </el-table-column>
  132. <el-table-column
  133. align="center"
  134. prop="playTimeRather240"
  135. sortable="custom"
  136. label=">240分钟"
  137. width="120px"
  138. >
  139. <template slot-scope="scope">
  140. <div>{{ scope.row.playTimeRather240 }}人</div>
  141. </template>
  142. </el-table-column>
  143. <el-table-column
  144. align="center"
  145. prop="avgPlayTime"
  146. sortable="custom"
  147. label="平均时长"
  148. width="100px"
  149. >
  150. <template slot-scope="scope">
  151. <div>{{ scope.row.avgPlayTime || 0 }}分钟</div>
  152. </template>
  153. </el-table-column>
  154. <el-table-column align="center" label="操作" fixed="right">
  155. <template slot-scope="scope">
  156. <el-button
  157. v-permission="'/coursewareDurationDetail'"
  158. @click="onDetail(scope.row)"
  159. type="text"
  160. >详情</el-button
  161. >
  162. </template>
  163. </el-table-column>
  164. </el-table>
  165. </div>
  166. </div>
  167. </template>
  168. <script>
  169. import { Export } from "@/utils/downLoadFile";
  170. import saveform from "@/components/save-form";
  171. import dayjs from "dayjs";
  172. import histogram from "v-charts/lib/histogram.common";
  173. import { getNowDateAndMonday, getNowDateAndSunday } from "@/utils/utils";
  174. import { api_studentCoursewarePlayRecordList } from "../api";
  175. import { getTimeFormat } from "@/utils";
  176. let nowTime = dayjs()
  177. .subtract(1, "day")
  178. .format("YYYY-MM-DD");
  179. // export const getTimeFormat = (times, keys = []) => {
  180. // if (times && times.length) {
  181. // return {
  182. // [keys[0] || "start"]: dayjs(times[0]).format("YYYY-MM-DD"),
  183. // [keys[1] || "start"]: dayjs(times[1]).format("YYYY-MM-DD")
  184. // };
  185. // }
  186. // return {};
  187. // };
  188. export const getCurrentMonth = () => {
  189. return [
  190. dayjs()
  191. .startOf("month")
  192. .format("YYYY-MM-DD"),
  193. dayjs()
  194. .endOf("month")
  195. .format("YYYY-MM-DD")
  196. ];
  197. };
  198. export default {
  199. components: { saveform, "ve-histogram": histogram },
  200. name: "helpCategory",
  201. data() {
  202. return {
  203. searchForm: {
  204. dates: getCurrentMonth(),
  205. sort: null,
  206. asc: null
  207. },
  208. tableList: []
  209. };
  210. },
  211. async mounted() {
  212. this.getList();
  213. },
  214. methods: {
  215. onSortChange(val) {
  216. const template = {
  217. memberNum: 1,
  218. noPlayNum: 2,
  219. playTimeLess10: 3,
  220. playTimeLess60: 4,
  221. playTimeLess120: 5,
  222. playTimeLess240: 6,
  223. playTimeRather240: 7,
  224. avgPlayTime: 8
  225. };
  226. this.searchForm.sort = template[val.prop];
  227. if (val.order === "ascending") {
  228. this.searchForm.asc = true;
  229. } else if (val.order === "descending") {
  230. this.searchForm.asc = false;
  231. } else {
  232. this.searchForm.asc = null;
  233. this.searchForm.sort = null;
  234. }
  235. this.getList();
  236. },
  237. exportQuestion() {
  238. const { dates } = this.searchForm;
  239. Export(
  240. this,
  241. {
  242. url: "/api-web/studentCoursewarePlayRecord/exportStatList",
  243. fileName: "云课堂观看统计.xls",
  244. method: "post",
  245. params: {
  246. ...getTimeFormat(dates, ["startTime", "endTime"])
  247. // exportEnum: "VIDEO_PLAY_STAT",
  248. // queryInfo: {
  249. // ...getTimeFormat(dates, ["startTime", "endTime"])
  250. // }
  251. }
  252. },
  253. "您确定导出云课堂观看统计?"
  254. );
  255. },
  256. onDetail(row) {
  257. this.$router.push({
  258. path: "/coursewareDurationDetail",
  259. query: {
  260. organId: row.organizationId,
  261. dates: JSON.stringify(this.searchForm.dates)
  262. }
  263. });
  264. },
  265. search() {
  266. this.$refs.searchForm.validate(valid => {
  267. this.pageInfo = {
  268. ...this.pageInfo,
  269. page: 1
  270. };
  271. this.getList();
  272. });
  273. },
  274. onReSet() {
  275. this.pageInfo = {
  276. ...this.pageInfo,
  277. page: 1
  278. };
  279. this.$refs.searchForm.resetFields();
  280. this.getList();
  281. },
  282. getDefaultTime() {
  283. const dayjs = this.$helpers.dayjs;
  284. let nowDate = dayjs(new Date()).format("YYYY-MM-DD");
  285. let lastWeek = dayjs(nowDate)
  286. .subtract(1, "week")
  287. .format("YYYY-MM-DD");
  288. console.log(lastWeek, nowDate, "121212");
  289. this.searchForm.dates = [
  290. getNowDateAndMonday(lastWeek),
  291. getNowDateAndSunday(nowDate)
  292. ];
  293. console.log(this.searchForm.dates, "this.searchForm.dates");
  294. },
  295. changeWeek(val) {
  296. if (val) {
  297. // this.searchForm.dates = [
  298. // getNowDateAndMonday(val[0]),
  299. // getNowDateAndSunday(val[1])
  300. // ];
  301. } else {
  302. this.getDefaultTime();
  303. }
  304. },
  305. async getList() {
  306. try {
  307. const { dates, sort, asc } = this.searchForm;
  308. let params = {
  309. sort,
  310. asc,
  311. ...getTimeFormat(dates, ["startTime", "endTime"])
  312. };
  313. const res = await api_studentCoursewarePlayRecordList({
  314. page: 1,
  315. rows: 10,
  316. ...params
  317. });
  318. this.tableList = [];
  319. this.tableList = res.data;
  320. } catch {}
  321. }
  322. },
  323. computed: {
  324. // items() {
  325. // let obj = {};
  326. // let arr = [
  327. // "normalNum",
  328. // "train1",
  329. // "train2",
  330. // "train3",
  331. // "train4",
  332. // "buyRate",
  333. // "avgTrainTime"
  334. // ];
  335. // arr.forEach(str => {
  336. // if (this.dataList[str] + "") {
  337. // obj[str] = {
  338. // title: titles[str],
  339. // percent: this.dataList[str],
  340. // desc: descs[str]
  341. // };
  342. // }
  343. // });
  344. // return obj;
  345. // },
  346. // scaleItems() {
  347. // let obj = {};
  348. // let arr = ["trainRate", "trainStandRate"];
  349. // arr.forEach(str => {
  350. // if (this.dataList[str] + "") {
  351. // obj[str] = {
  352. // title: titles[str],
  353. // percent: this.dataList[str],
  354. // desc: descs[str]
  355. // };
  356. // }
  357. // });
  358. // return obj;
  359. // },
  360. legend() {
  361. return {
  362. left: "10px"
  363. };
  364. },
  365. chartData() {
  366. const temp = {
  367. noPlayNum: "无练习人数",
  368. playTimeLess10: "0-10分钟",
  369. playTimeLess60: "10-60分钟",
  370. playTimeLess120: "60-120分钟",
  371. playTimeLess240: "120-240分钟",
  372. playTimeRather240: ">240分钟",
  373. avgPlayTime: "平均时长"
  374. };
  375. const values = this.tableList;
  376. const months = {};
  377. for (const key in temp) {
  378. for (const item of values) {
  379. if (!months[item.organizationName]) {
  380. months[item.organizationName] = {
  381. 分部: item.organizationName
  382. };
  383. }
  384. months[item.organizationName][temp[key]] = item[key] || 0;
  385. }
  386. }
  387. console.log(months, "months");
  388. return {
  389. columns: [
  390. "分部",
  391. "无练习人数",
  392. "0-10分钟",
  393. "10~60分钟",
  394. "60-120分钟",
  395. "120-240分钟",
  396. ">240分钟",
  397. "平均时长"
  398. ],
  399. rows: Object.values(months)
  400. };
  401. },
  402. chartExtend() {
  403. return {
  404. yAxis: {
  405. //纵轴标尺固定
  406. minInterval: 1,
  407. type: "value",
  408. scale: true,
  409. min: 0,
  410. axisLabel: {
  411. formatter: "{value}"
  412. }
  413. },
  414. series: {
  415. type: "bar",
  416. smooth: false
  417. },
  418. tooltip: {
  419. axisPointer: {
  420. type: "shadow",
  421. shadowStyle: {
  422. color: "rgba(150,150,150,0.2)"
  423. }
  424. },
  425. formatter: item => {
  426. // let arr = [
  427. // { name: "会员人数", dot: "元" },
  428. // { name: "<60分钟", dot: "%" },
  429. // { name: "60-120分钟", dot: "元" },
  430. // { name: "120-240分钟", dot: "元" },
  431. // { name: ">240分钟", dot: "元" },
  432. // { name: "平均时长", dot: "元" }
  433. // ];
  434. // console.log(item, "-121212--------------");
  435. return [
  436. item[0].axisValueLabel,
  437. ...item.map(d => {
  438. return `<br/>${d.marker}${d.seriesName}: ${d.value}${
  439. d.seriesName == "平均时长" ? "分钟" : "人"
  440. }`;
  441. })
  442. ].join("");
  443. }
  444. }
  445. };
  446. },
  447. scalChartExtend() {
  448. return {
  449. yAxis: {
  450. //纵轴标尺固定
  451. minInterval: 1,
  452. type: "value",
  453. scale: true,
  454. min: 0,
  455. axisLabel: {
  456. formatter: "{value}%"
  457. }
  458. },
  459. series: {
  460. type: "bar",
  461. smooth: false
  462. },
  463. tooltip: {
  464. axisPointer: {
  465. type: "shadow",
  466. shadowStyle: {
  467. color: "rgba(150,150,150,0.2)"
  468. }
  469. },
  470. formatter: item => {
  471. return [
  472. item[0].axisValueLabel,
  473. ...item.map(d => {
  474. return `<br/>${d.marker}${d.seriesName}: ${d.value}%`;
  475. })
  476. ].join("");
  477. }
  478. }
  479. };
  480. },
  481. dataZoom() {
  482. return [
  483. {
  484. show: true,
  485. type: "slider",
  486. start: 0,
  487. end: 30,
  488. filterMode: "empty",
  489. zoomLock: true,
  490. handleSize: 0
  491. }
  492. ];
  493. },
  494. dataEmpty() {
  495. return !this.chartData.rows.length;
  496. },
  497. exporyun() {
  498. return {
  499. organId: this.organId
  500. };
  501. }
  502. }
  503. };
  504. </script>
  505. <style lang="scss" scoped>
  506. ::v-deep .el-date-editor.el-input {
  507. width: 100% !important;
  508. }
  509. ::v-deep .el-select {
  510. width: 100% !important;
  511. }
  512. ::v-deep .el-table .cell {
  513. display: -webkit-box;
  514. overflow: hidden;
  515. max-height: 45px;
  516. text-overflow: ellipsis;
  517. -webkit-line-clamp: 3;
  518. -webkit-box-orient: vertical;
  519. }
  520. ::v-deep .el-dialog__body {
  521. padding: 10px 20px;
  522. }
  523. .newBand {
  524. display: inline-block;
  525. }
  526. ::v-deep .el-tabs__active-bar {
  527. background-color: transparent !important;
  528. }
  529. ::v-deep.el-tabs__nav-wrap {
  530. &:after {
  531. background-color: transparent !important;
  532. }
  533. }
  534. /*去掉切换时el-tab-pane底部的蓝色下划线*/
  535. ::v-deep .el-tabs__nav-wrap::after {
  536. background-color: transparent !important;
  537. }
  538. /*去掉tabs底部的下划线*/
  539. </style>