Skill.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. <template>
  2. <div>
  3. <el-card>
  4. <el-form :model="skillParams" ref="skillParams" class="clearfix topForm" style="float: left">
  5. <div class="leftForm-skill">
  6. <el-form-item
  7. style="display:flex;"
  8. prop="channel"
  9. name="channel"
  10. :rules="[{ required: true, message: '请选择渠道', trigger: 'blur' }]"
  11. label="渠道选择"
  12. >
  13. <el-select
  14. v-model="skillParams.channel"
  15. @change="channelChange"
  16. style="width:160px"
  17. placeholder="请选择渠道"
  18. >
  19. <el-option
  20. v-for="item in skillChanneList"
  21. :key="item.code"
  22. :label="item.title"
  23. :value="item.code"
  24. ></el-option>
  25. </el-select>
  26. </el-form-item>
  27. <el-form-item
  28. class="pruductForm-skill"
  29. prop="skillId"
  30. label="技能选择"
  31. v-if="hide(skillParams.channel)"
  32. :rules="[{ required: true, message: '请选择技能', trigger: 'blur' }]"
  33. >
  34. <!-- 兼容小度在家和学有义方不同的返回内容 -->
  35. <el-select v-model="skillParams.skillId" placeholder="请选择技能" @change="skillChange" style="width:160px">
  36. <el-option
  37. v-for="item in skillList"
  38. :key="item.skillId || item.skillId === 0 ? item.skillId : item.id"
  39. :label="item.skillName ? item.skillName : item.title"
  40. :value="item.skillId || item.skillId === 0 ? item.skillId : item.id"
  41. ></el-option>
  42. </el-select>
  43. </el-form-item>
  44. <!-- <el-form-item
  45. class="pruductForm-skill"
  46. prop="classId"
  47. label="产品包选择"
  48. v-if="skillParams.skillId === 0 || skillParams.skillId === 3"
  49. :rules="[{ required: true, message: '请选择产品包', trigger: 'blur' }]"
  50. >
  51. 兼容小度在家和学有义方不同的返回内容
  52. <el-select v-model="skillParams.classId" placeholder="请选择产品包" style="width:160px">
  53. <el-option
  54. v-for="item in baiduCourseList"
  55. :key="item.id"
  56. :label="item.fullName"
  57. :value="item.id"
  58. ></el-option>
  59. </el-select>
  60. </el-form-item> -->
  61. <el-form-item class="dateForm-skill" name="data" label="查询日期" style="display:flex">
  62. <el-date-picker
  63. style="width:140px"
  64. v-model="skillParams.startDate"
  65. type="date"
  66. placeholder="起"
  67. ></el-date-picker>
  68. <el-date-picker
  69. style="width:140px"
  70. v-model="skillParams.endDate"
  71. type="date"
  72. placeholder="止"
  73. ></el-date-picker>
  74. </el-form-item>
  75. </div>
  76. <el-form-item>
  77. <el-button type="primary" @click="submitForm('skillParams')">搜索</el-button>
  78. </el-form-item>
  79. </el-form>
  80. <el-button type="primary" @click="onExportExcel" style="float: right">导出</el-button>
  81. <el-table id="table" :data="skillData" border style="width: 100%" :height="tableHeight" v-if="skillParams.channel === 'XYYF'" >
  82. <el-table-column
  83. label="日期"
  84. fixed
  85. align="left"
  86. header-align="center"
  87. min-width="140px"
  88. >
  89. <template slot-scope="scope">
  90. <span>{{changeDate(scope.row.day)}}</span>
  91. </template>
  92. </el-table-column>
  93. <el-table-column label="流量与观看" header-align="center">
  94. <el-table-column
  95. label="UV"
  96. prop="uv"
  97. align="right"
  98. header-align="center"
  99. min-width="100px"
  100. ></el-table-column>
  101. <el-table-column
  102. label="VIP用户 | -UV1"
  103. align="right"
  104. header-align="center"
  105. :render-header="renderheader"
  106. prop="vipUv"
  107. min-width="120px"
  108. ></el-table-column>
  109. <el-table-column
  110. label="非VIP用户 | UV2=UV-UV1"
  111. align="right"
  112. header-align="center"
  113. :render-header="renderheader"
  114. prop="waitVipUv"
  115. min-width="140px"
  116. ></el-table-column>
  117. <el-table-column
  118. label="VIP用户占比 | =UV1/UV"
  119. align="right"
  120. header-align="center"
  121. :render-header="renderheader"
  122. prop="vipProportion"
  123. min-width="140px"
  124. ></el-table-column>
  125. <el-table-column
  126. label="VIP权限用户 | =V0"
  127. align="right"
  128. header-align="center"
  129. :render-header="renderheader"
  130. prop="vipCount"
  131. min-width="140px"
  132. ></el-table-column>
  133. <el-table-column
  134. label="VIP用户活跃率 | =UV1/V0"
  135. align="right"
  136. header-align="center"
  137. :render-header="renderheader"
  138. min-width="140px"
  139. prop="aliveVipProportion"
  140. >
  141. </el-table-column>
  142. <!-- <el-table-column
  143. label="VV"
  144. prop="vv"
  145. align="right"
  146. header-align="center"
  147. min-width="100px"
  148. ></el-table-column>-->
  149. <el-table-column
  150. label="VV"
  151. align="right"
  152. header-align="center"
  153. :render-header="renderheader"
  154. prop="vv"
  155. min-width="140px"
  156. ></el-table-column>
  157. <el-table-column
  158. label="忠实用户"
  159. align="right"
  160. header-align="center"
  161. :render-header="renderheader"
  162. prop="fealtyCount"
  163. min-width="140px"
  164. ></el-table-column>
  165. <el-table-column
  166. label="忠实用户占比"
  167. align="right"
  168. header-align="center"
  169. :render-header="renderheader"
  170. prop="fealtyProportion"
  171. min-width="140px"
  172. ></el-table-column>
  173. <el-table-column
  174. label="人均播放 | =VV/UV"
  175. align="right"
  176. header-align="center"
  177. :render-header="renderheader"
  178. prop="perCapitaPlay"
  179. min-width="140px"
  180. ></el-table-column>
  181. </el-table-column>
  182. <el-table-column label="拉新,付费与转换率" header-align="center">
  183. <el-table-column
  184. label="连续包月 | -Qm1"
  185. align="right"
  186. header-align="center"
  187. :render-header="renderheader"
  188. prop="consecutiveMonthly"
  189. min-width="100px"
  190. ></el-table-column>
  191. <el-table-column
  192. label="新增连续包月 | -Qm1-1"
  193. align="right"
  194. header-align="center"
  195. :render-header="renderheader"
  196. min-width="140px"
  197. >
  198. <template slot-scope="scope">
  199. <span>{{scope.row.appendConsecutiveMonthly || 0}}</span>
  200. </template>
  201. </el-table-column>
  202. <el-table-column
  203. label="续包月续费 | -Qm1-2"
  204. align="right"
  205. header-align="center"
  206. :render-header="renderheader"
  207. min-width="140px"
  208. >
  209. <template slot-scope="scope">
  210. <span>{{scope.row.continueConsecutiveMonthly || 0}}</span>
  211. </template>
  212. </el-table-column>
  213. <el-table-column
  214. label="月包 | -Qm2"
  215. align="right"
  216. header-align="center"
  217. :render-header="renderheader"
  218. prop="monthlyPayment"
  219. min-width="90px"
  220. ></el-table-column>
  221. <el-table-column
  222. label="年包 | -Qy"
  223. align="right"
  224. header-align="center"
  225. :render-header="renderheader"
  226. prop="yearlyPayment"
  227. ></el-table-column>
  228. <el-table-column
  229. label="付费订单数量| Q=Qm1+Qm2+Qy"
  230. align="right"
  231. header-align="center"
  232. :render-header="renderheader"
  233. prop="totalPayment"
  234. min-width="160px"
  235. ></el-table-column>
  236. <el-table-column
  237. label="r1 | =Qm1/Q"
  238. align="right"
  239. header-align="center"
  240. :render-header="renderheader"
  241. prop="consecutiveConversion"
  242. min-width="130px"
  243. ></el-table-column>// 月包占比
  244. <el-table-column
  245. label="r2 | =Qm2/Q"
  246. align="right"
  247. header-align="center"
  248. :render-header="renderheader"
  249. prop="monthlyConversion"
  250. min-width="130px"
  251. ></el-table-column>// 年包占比
  252. <el-table-column
  253. label="r3 | =Qy/Q"
  254. align="right"
  255. header-align="center"
  256. :render-header="renderheader"
  257. prop="yearlyConversion"
  258. min-width="130px"
  259. ></el-table-column>
  260. <el-table-column
  261. label="R | =(Qm1+Qm2+Qy)/UV2"
  262. align="right"
  263. header-align="center"
  264. :render-header="renderheader"
  265. prop="totalConversion"
  266. min-width="190px"
  267. ></el-table-column>
  268. <el-table-column
  269. label="R1 | =(Qm1-1+Qm2+Qy)/UV2"
  270. align="right"
  271. header-align="center"
  272. :render-header="renderheader"
  273. min-width="190px"
  274. >
  275. <template slot-scope="scope">
  276. <span>{{scope.row.appendTotalConversion || 0}}</span>
  277. </template>
  278. </el-table-column>
  279. </el-table-column>
  280. </el-table>
  281. <el-table id="table" :data="skillData" border style="width: 100%" :height="tableHeight" v-else>
  282. <el-table-column
  283. label="日期"
  284. fixed
  285. align="left"
  286. header-align="center"
  287. min-width="140px"
  288. >
  289. <template slot-scope="scope">
  290. <span>{{changeDate(scope.row.day)}}</span>
  291. </template>
  292. </el-table-column>
  293. <el-table-column label="流量与观看" header-align="center">
  294. <el-table-column
  295. label="UV"
  296. prop="uv"
  297. align="right"
  298. header-align="center"
  299. min-width="100px"
  300. ></el-table-column>
  301. <el-table-column
  302. label="VIP用户 | -UV1"
  303. align="right"
  304. header-align="center"
  305. :render-header="renderheader"
  306. prop="vipUv"
  307. min-width="120px"
  308. ></el-table-column>
  309. <el-table-column
  310. label="非VIP用户 | UV2=UV-UV1"
  311. align="right"
  312. header-align="center"
  313. :render-header="renderheader"
  314. prop="waitVipUv"
  315. min-width="140px"
  316. ></el-table-column>
  317. <el-table-column
  318. label="VIP用户占比 | =UV1/UV"
  319. align="right"
  320. header-align="center"
  321. :render-header="renderheader"
  322. prop="vipProportion"
  323. min-width="140px"
  324. ></el-table-column>
  325. <!-- <el-table-column
  326. label="VV"
  327. prop="vv"
  328. align="right"
  329. header-align="center"
  330. min-width="100px"
  331. ></el-table-column>-->
  332. <el-table-column
  333. label="VV"
  334. align="right"
  335. header-align="center"
  336. :render-header="renderheader"
  337. prop="totalPlay"
  338. min-width="140px"
  339. ></el-table-column>
  340. <el-table-column
  341. label="人均播放 | =VV/UV"
  342. align="right"
  343. header-align="center"
  344. :render-header="renderheader"
  345. prop="perCapitaPlay"
  346. min-width="140px"
  347. ></el-table-column>
  348. </el-table-column>
  349. <el-table-column label="拉新,付费与转换率" header-align="center">
  350. <el-table-column
  351. label="连续包月 | -Qm1"
  352. align="right"
  353. header-align="center"
  354. :render-header="renderheader"
  355. prop="consecutiveMonthly"
  356. min-width="100px"
  357. ></el-table-column>
  358. <el-table-column
  359. label="新增连续包月 | -Qm1-1"
  360. align="right"
  361. header-align="center"
  362. :render-header="renderheader"
  363. min-width="140px"
  364. >
  365. <template slot-scope="scope">
  366. <span>{{scope.row.appendConsecutiveMonthly || 0}}</span>
  367. </template>
  368. </el-table-column>
  369. <el-table-column
  370. label="续包月续费 | -Qm1-2"
  371. align="right"
  372. header-align="center"
  373. :render-header="renderheader"
  374. prop="continueConsecutiveMonthly"
  375. min-width="140px"
  376. >
  377. <template slot-scope="scope">
  378. <span>{{scope.row.continueConsecutiveMonthly || 0}}</span>
  379. </template>
  380. </el-table-column>
  381. <el-table-column
  382. label="月包 | -Qm2"
  383. align="right"
  384. header-align="center"
  385. :render-header="renderheader"
  386. prop="monthlyPayment"
  387. min-width="90px"
  388. ></el-table-column>
  389. <el-table-column
  390. label="年包 | -Qy"
  391. align="right"
  392. header-align="center"
  393. :render-header="renderheader"
  394. prop="yearlyPayment"
  395. ></el-table-column>
  396. <el-table-column
  397. label="付费订单数量| Q=Qm1+Qm2+Qy"
  398. align="right"
  399. header-align="center"
  400. :render-header="renderheader"
  401. prop="totalPayment"
  402. min-width="160px"
  403. ></el-table-column>
  404. <el-table-column
  405. label="r1 | =Qm1/Q"
  406. align="right"
  407. header-align="center"
  408. :render-header="renderheader"
  409. prop="consecutiveConversion"
  410. min-width="130px"
  411. ></el-table-column>// 月包占比
  412. <el-table-column
  413. label="r2 | =Qm2/Q"
  414. align="right"
  415. header-align="center"
  416. :render-header="renderheader"
  417. prop="monthlyConversion"
  418. min-width="130px"
  419. ></el-table-column>// 年包占比
  420. <el-table-column
  421. label="r3 | =Qy/Q"
  422. align="right"
  423. header-align="center"
  424. :render-header="renderheader"
  425. prop="yearlyConversion"
  426. min-width="130px"
  427. ></el-table-column>
  428. <el-table-column
  429. label="R | =(Qm1+Qm2+Qy)/UV2"
  430. align="right"
  431. header-align="center"
  432. :render-header="renderheader"
  433. prop="totalConversion"
  434. min-width="190px"
  435. ></el-table-column>
  436. <el-table-column
  437. label="R1 | =(Qm1-1+Qm2+Qy)/UV2"
  438. align="right"
  439. header-align="center"
  440. :render-header="renderheader"
  441. min-width="190px"
  442. >
  443. <template slot-scope="scope">
  444. <span>{{scope.row.appendTotalConversion || 0}}</span>
  445. </template>
  446. </el-table-column>
  447. </el-table-column>
  448. </el-table>
  449. <!-- <el-pagination
  450. v-if="orderList.totalElements"
  451. background
  452. layout="prev, pager, next"
  453. :total="orderList.totalElements"
  454. @current-change="changePage">
  455. </el-pagination>-->
  456. </el-card>
  457. </div>
  458. </template>
  459. <script>
  460. import { mapGetters } from "vuex";
  461. import formatDate from "../../utils/formatTime";
  462. import { getBaiduCourseList } from '../../api/skill'
  463. import downTable from '@/utils/downTable'
  464. export default {
  465. data() {
  466. return {
  467. tableHeight: 500,
  468. dateValue: "",
  469. skillParams: {
  470. skillId: "",
  471. startDate: "",
  472. endDate: "",
  473. channel: "",
  474. classId: ""
  475. },
  476. channelList: "",
  477. skillChanneList: [
  478. { title: "百度-小度在家", code: "BAIDU" },
  479. { title: "阿里-天猫精灵", code: "ALI" },
  480. { title: "百度-义方小学堂", code: "XYYF" },
  481. { title: "OPPO-小学同步辅导", code: "6001" }
  482. ],
  483. baiduCourseList: []
  484. };
  485. },
  486. computed: {
  487. ...mapGetters({
  488. skillList: "skillList",
  489. orderList: "orderList",
  490. productList: "productList",
  491. skillData: "skillData"
  492. })
  493. },
  494. created() {
  495. // this.$store.dispatch("getOrderList", this.skillParams);
  496. this.skillParams.startDate = this.getYesterDay();
  497. this.skillParams.endDate = this.getYesterDay();
  498. this.tableHeight = document.documentElement.clientHeight * 0.75;
  499. },
  500. methods: {
  501. // 搜索
  502. submitForm(formName) {
  503. this.skillParams.startDate = this.skillParams.startDate
  504. ? formatDate(this.skillParams.startDate, 2)
  505. : "";
  506. this.skillParams.endDate = this.skillParams.endDate
  507. ? formatDate(this.skillParams.endDate, 2)
  508. : "";
  509. this.$refs[formName].validate(valid => {
  510. if (valid) {
  511. if(this.skillParams.channel === '6001'){
  512. this.$store.dispatch("getOppoData", this.skillParams);
  513. } else if(this.skillParams.skillId === 0 || this.skillParams.skillId === 3){
  514. // this.$store.dispatch("getBaiduData", this.skillParams);
  515. this.$store.dispatch("getSkillData", this.skillParams);
  516. } else if(this.skillParams.channel === 'ALI') {
  517. this.$store.dispatch("getAliData", this.skillParams);
  518. } else{
  519. this.$store.dispatch("getSkillData", this.skillParams);
  520. }
  521. } else {
  522. console.log("error submit!!");
  523. return false;
  524. }
  525. });
  526. },
  527. // 渠道下课程分页
  528. changePage(e) {
  529. this.skillParams.pageNo = e;
  530. this.$store.dispatch("getSkillData", this.skillParams);
  531. },
  532. // 表头折行
  533. renderheader(h, { column, $index }) {
  534. return h("span", {}, [
  535. h("span", {}, column.label.split("|")[0]),
  536. h("br"),
  537. h("span", {}, column.label.split("|")[1])
  538. ]);
  539. },
  540. getYesterDay() {
  541. let yesterday = new Date().getTime() - 86400000;
  542. return formatDate(yesterday, 2);
  543. },
  544. channelChange(val) {
  545. this.skillParams.skillId = "";
  546. val !== 'XYYF' && this.$store.dispatch("getSkillList", val);
  547. },
  548. skillChange(val) {
  549. this.skillParams.classId = ''
  550. console.log(val)
  551. // if (val === 0) {
  552. // getBaiduCourseList('CHINESE').then(res => {
  553. // this.baiduCourseList = [{
  554. // fullName: '全部',
  555. // id: 100
  556. // }, ...res.data]
  557. // })
  558. // } else if (val === 3) {
  559. // getBaiduCourseList('MATH').then(res => {
  560. // this.baiduCourseList = [{
  561. // fullName: '全部',
  562. // id: 101
  563. // }, ...res.data]
  564. // })
  565. // }
  566. },
  567. columnZero() {
  568. return "0";
  569. },
  570. columnZeroPercent(row, column, index) {
  571. if (row.totalPayment && column.property === "yearlyConversion") {
  572. return "100.00%";
  573. } else {
  574. return "0.00%";
  575. }
  576. // if(row.)
  577. },
  578. hide (channel) {
  579. if (channel === '6001' || channel === 'XYYF') {
  580. return false
  581. }
  582. return true
  583. },
  584. changeDate(date) {
  585. return formatDate(date, 4)
  586. },
  587. // 导出表格
  588. onExportExcel() {
  589. const nameList = this.skillChanneList.filter((item) => item.code === this.skillParams.channel)[0]
  590. const name = (nameList && nameList.title) || ''
  591. const list = this.skillList.filter((item) => item.skillId === this.skillParams.skillId)[0] || ''
  592. const productName = list && list.skillName ? '-' + list.skillName : ''
  593. const date = formatDate(new Date(), 7)
  594. downTable('table', [
  595. {wch: 15}, // "characters"
  596. {wch: 6},
  597. {wch: 15},
  598. {wch: 20},
  599. {wch: 20},
  600. {wch: 6},
  601. {wch: 15},
  602. {wch: 15},
  603. {wch: 18},
  604. {wch: 18},
  605. {wch: 10},
  606. {wch: 15},
  607. {wch: 25},
  608. {wch: 10},
  609. {wch: 10},
  610. {wch: 10},
  611. {wch: 20},
  612. {wch: 25}
  613. ], name + productName + date)
  614. }
  615. }
  616. };
  617. </script>
  618. <style lang="less" scoped>
  619. .pruductForm-skill {
  620. margin-left: 10px;
  621. display: flex;
  622. }
  623. .topForm {
  624. display: flex;
  625. align-items: center;
  626. flex-direction: row;
  627. flex-shrink: 0;
  628. justify-content: space-between;
  629. }
  630. .leftForm-skill {
  631. display: flex;
  632. align-items: center;
  633. flex-direction: row;
  634. justify-content: space-between;
  635. }
  636. .dateForm-skill {
  637. margin-left: 18px;
  638. }
  639. </style>