maincomponents.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. <template>
  2. <div class="maincomponents" ref="main">
  3. <div class="card">
  4. <div class="cardtit">传播数据</div>
  5. <el-row style="margin: 10px 10px 0 10px">
  6. <el-col :span="4">
  7. <el-card class="box-card" style="text-align: center">
  8. <div class="numTitle">{{ playT | numform }}</div>
  9. <div class="numValue">总传播量</div>
  10. </el-card>
  11. <br />
  12. <el-card class="box-card" style="text-align: center">
  13. <div class="numTitle">{{ publishCount | numform }}</div>
  14. <div class="numValue">总发稿量</div>
  15. </el-card>
  16. </el-col>
  17. <el-col :span="20" style="padding-left: 10px">
  18. <el-card class="box-card">
  19. <div class="pl">
  20. <div class="platform" v-for="(item, i) in plays" :key="i">
  21. <div class="line" v-if="i % 4 !== 0"></div>
  22. <div class="numTitle" :style="'color:' + c[i % c.length]">
  23. {{ item.val | numform }}
  24. </div>
  25. <div class="numValue">{{ item.p }}</div>
  26. </div>
  27. </div>
  28. </el-card>
  29. </el-col>
  30. </el-row>
  31. </div>
  32. <el-row>
  33. <el-col :span="15">
  34. <div class="card">
  35. <div class="cardtit">传播量趋势</div>
  36. <canvas id="container" style="width: 100%; height: 100%" class=""></canvas>
  37. </div>
  38. </el-col>
  39. <el-col :span="9" style="padding-left: 5px">
  40. <div class="card">
  41. <div class="cardtit">传播平台占比</div>
  42. <canvas
  43. id="piechart"
  44. style="width: 100%; height: 100%"
  45. class=""
  46. :height="height + 'px'"
  47. ></canvas>
  48. </div>
  49. </el-col>
  50. </el-row>
  51. <div class="card">
  52. <div class="cardtit">
  53. 部门传播量排行
  54. <!-- <el-button-group style="float: right; margin-right: 5px">
  55. <el-button
  56. :type="type == 'ALL' ? 'primary' : ''"
  57. size="small"
  58. @click="type = 'ALL'"
  59. >
  60. 全部
  61. </el-button>
  62. <el-button
  63. :type="type == 'MONTH' ? 'primary' : ''"
  64. size="small"
  65. @click="type = 'MONTH'"
  66. >
  67. </el-button>
  68. <el-button
  69. :type="type == 'WEEK' ? 'primary' : ''"
  70. size="small"
  71. @click="type = 'WEEK'"
  72. >
  73. </el-button>
  74. </el-button-group> -->
  75. </div>
  76. <canvas id="barChart" v-if="heightBar > 0" :height="heightBar" style="width: 100%"></canvas>
  77. </div>
  78. <div class="card">
  79. <div class="cardtit">热点报道</div>
  80. <div style="padding: 10px">
  81. <el-table :data="platformList" style="width: 100%">
  82. <el-table-column show-overflow-tooltip prop="host" label="部门"> </el-table-column>
  83. <el-table-column show-overflow-tooltip prop="subTopic" label="热点话题">
  84. </el-table-column>
  85. <el-table-column show-overflow-tooltip prop="playCount" label="传播量">
  86. <template slot-scope="scope">
  87. <el-popover placement="right" width="400" trigger="hover">
  88. <el-table :data="scope.row.topicDataList">
  89. <el-table-column property="platform" label="平台"></el-table-column>
  90. <el-table-column property="address" label="传播量">
  91. <template slot-scope="scopeSon">
  92. {{ scopeSon.row.playCount | numform }}
  93. </template>
  94. </el-table-column>
  95. </el-table>
  96. <div slot="reference">
  97. {{ scope.row.playCount | numform }}
  98. <i class="el-icon-warning" style="color: #1890ff"></i>
  99. </div>
  100. </el-popover>
  101. </template>
  102. </el-table-column>
  103. </el-table>
  104. </div>
  105. </div>
  106. <div class="card">
  107. <div class="cardtit">
  108. 发稿任务统计
  109. <el-select
  110. v-model="dep"
  111. style="width: 10em; float: right; margin-right: 10px"
  112. placeholder="请选择部门"
  113. @change="changeselect"
  114. size="small"
  115. >
  116. <el-option
  117. v-for="item in options"
  118. :key="item.value"
  119. :label="item.label"
  120. :value="item.value"
  121. >
  122. </el-option>
  123. </el-select>
  124. <el-date-picker
  125. v-model="T"
  126. type="daterange"
  127. @change="change"
  128. style="float: right; margin-right: 10px"
  129. value-format="yyyy-MM-dd HH:mm:ss"
  130. range-separator="-"
  131. size="small"
  132. start-placeholder="开始日期"
  133. end-placeholder="结束日期"
  134. >
  135. </el-date-picker>
  136. </div>
  137. <div style="padding: 20px">
  138. <el-table v-loading="loading" :data="userlist" style="width: 100%">
  139. <el-table-column show-overflow-tooltip prop="depName" label="部门"> </el-table-column>
  140. <el-table-column show-overflow-tooltip prop="name" label="账号"> </el-table-column>
  141. <el-table-column show-overflow-tooltip prop="platform" label="平台"> </el-table-column>
  142. <el-table-column show-overflow-tooltip prop="publishTime" label="发稿时间">
  143. </el-table-column>
  144. </el-table>
  145. <el-pagination
  146. style="margin-top: 5px"
  147. background
  148. layout="total, prev, pager, next"
  149. :total="usertotal"
  150. :current-page="page"
  151. @current-change="pagechange"
  152. >
  153. </el-pagination>
  154. </div>
  155. </div>
  156. <div class="card">
  157. <div class="cardtit">TOP30</div>
  158. <div style="padding: 20px">
  159. <el-table v-loading="loading" :data="top" style="width: 100%">
  160. <el-table-column show-overflow-tooltip prop="title" label="标题" width="300">
  161. </el-table-column>
  162. <el-table-column show-overflow-tooltip prop="platform" label="平台" width="50">
  163. </el-table-column>
  164. <el-table-column show-overflow-tooltip prop="depName" label="部门"> </el-table-column>
  165. <el-table-column show-overflow-tooltip prop="name" label="账号"> </el-table-column>
  166. <el-table-column show-overflow-tooltip prop="readCount" label="阅读量" width="100">
  167. <template slot-scope="scope">
  168. {{ scope.row.readCount | numform }}
  169. </template>
  170. </el-table-column>
  171. <el-table-column width="150" show-overflow-tooltip prop="publishTime" label="发稿时间">
  172. </el-table-column>
  173. </el-table>
  174. </div>
  175. </div>
  176. </div>
  177. </template>
  178. <script>
  179. // @ is an alias to /src
  180. import F2 from '@antv/f2/lib/index-all';
  181. import {
  182. platformData,
  183. qushiData,
  184. bumenData,
  185. // pingtaiData,
  186. baodianList,
  187. zhanghaoData,
  188. bumenList,
  189. getTop
  190. } from '@/api/index.js';
  191. let linechart = null,
  192. barchart = null,
  193. piechart = null;
  194. export default {
  195. name: 'Maincomponents',
  196. data() {
  197. return {
  198. publishCount: 0,
  199. playT: 0,
  200. plays: [],
  201. loading: false,
  202. platformList: [],
  203. dep: '全部',
  204. T: [],
  205. userlist: [],
  206. type: localStorage.type || 'ALL',
  207. page: 1,
  208. options: [],
  209. top: [],
  210. pageSize: 10,
  211. usertotal: 0,
  212. height: 0,
  213. c: ['#FB6161', '#EC72DC', '#F98E53', '#49BED0', '#4BCA8B'],
  214. heightBar: 0
  215. };
  216. },
  217. filters: {
  218. numform(text) {
  219. let num = text;
  220. if (isNaN(text)) num = '0';
  221. if (text >= 100000000) num = (text / 100000000).toFixed(2) - 0 + '亿';
  222. else if (text >= 10000) num = (text / 10000).toFixed(2) - 0 + '万';
  223. else num = num + '';
  224. return num;
  225. }
  226. },
  227. props: ['hd', 'topic'],
  228. watch: {
  229. type() {
  230. this.bmGet();
  231. },
  232. hd() {
  233. this.init();
  234. }
  235. },
  236. mounted() {
  237. this.height = (this.$refs.main.offsetWidth / 24) * 6.9;
  238. },
  239. computed: {},
  240. methods: {
  241. init() {
  242. linechart && linechart.destroy();
  243. barchart && barchart.destroy();
  244. piechart && piechart.destroy();
  245. linechart = null;
  246. barchart = null;
  247. piechart = null;
  248. this.page = 1;
  249. let D = new Date(),
  250. Year = D.getFullYear(),
  251. Month = D.getMonth() + 1,
  252. Day = D.getDate();
  253. let oD = new Date(Date.now() - 86400000 * 30),
  254. oYear = oD.getFullYear(),
  255. oMonth = oD.getMonth() + 1,
  256. oDay = oD.getDate();
  257. this.T = [
  258. [oYear, oMonth, oDay].join('-') + ' 00:00:00',
  259. [Year, Month, Day].join('-') + ' 23:59:59'
  260. ];
  261. baodianList(this.topic).then(r => {
  262. this.platformList = r || [];
  263. });
  264. platformData(this.topic).then(r => {
  265. let platform = {},
  266. pli = [];
  267. for (let i = 0; i < (r || []).length; i++) {
  268. const v = (r || [])[i];
  269. if (v.platform === '总传播量') {
  270. this.playT = v.playCount || 0;
  271. continue;
  272. }
  273. if (!platform[v.platform] && platform[v.platform] !== 0) {
  274. platform[v.platform] = pli.length;
  275. pli.push({ p: v.platform, val: v.playCount, a: 1 });
  276. } else {
  277. pli[platform[v.platform]] && (pli[platform[v.platform]].val += v.playCount);
  278. }
  279. }
  280. this.plays = pli.sort((a, b) => b.val - a.val);
  281. if (!pli || !pli.length) {
  282. document.getElementById('piechart').style.display = 'none';
  283. } else {
  284. document.getElementById('piechart').style.display = 'block';
  285. }
  286. this.$nextTick(() => {
  287. this.pie1(pli);
  288. });
  289. });
  290. qushiData(this.topic).then(r => {
  291. let li = r || [];
  292. if (!li || !li.length) {
  293. document.getElementById('container').style.display = 'none';
  294. } else {
  295. document.getElementById('container').style.display = 'block';
  296. }
  297. this.$nextTick(() => {
  298. this.line1(li);
  299. });
  300. });
  301. this.bmGet();
  302. // pingtaiData({
  303. // depRange: "ALL",
  304. // title: this.topic,
  305. // }).then(r => {
  306. // console.log(r)
  307. // this.platformList = (r || []).sort((a, b) => b.readTotal - a.readTotal);
  308. // });
  309. this.grtzhData();
  310. bumenList().then(r => {
  311. let options = (r || []).map(v => {
  312. return {
  313. value: v.depName,
  314. label: v.depName
  315. };
  316. });
  317. options.unshift({
  318. value: '全部',
  319. label: '全部'
  320. });
  321. this.options = options;
  322. });
  323. getTop(this.topic).then(r => {
  324. this.top = r || [];
  325. });
  326. },
  327. change() {
  328. this.grtzhData();
  329. },
  330. changeselect() {
  331. this.grtzhData();
  332. },
  333. grtzhData() {
  334. this.loading = true;
  335. let t = (this.T || []).length ? this.T : ['', ''];
  336. const p = {
  337. page: this.page,
  338. pageSize: this.pageSize,
  339. title: this.topic
  340. };
  341. this.dep && this.dep !== '全部' && (p.depName = this.dep);
  342. t[0] && (p.startTime = t[0]);
  343. t[1] && (p.endTime = t[1]);
  344. if (p.endTime) p.endTime = p.endTime.replace('00:00:00', '23:59:59');
  345. zhanghaoData(p).then(r => {
  346. this.loading = false;
  347. this.userlist = r.data || [];
  348. this.usertotal = r.total || 0;
  349. });
  350. },
  351. bmGet() {
  352. bumenData({
  353. depRange: this.type,
  354. title: this.topic
  355. }).then(r => {
  356. let li = (r || []).reverse();
  357. this.heightBar = li.length ? li.length * 35 + 40 : 0;
  358. localStorage.type = this.type;
  359. if (this.heightBar === 0) return;
  360. let n = 0;
  361. (li || []).map(v => {
  362. n += v.publishCount;
  363. });
  364. this.publishCount = n;
  365. this.$nextTick(() => {
  366. this.bar1(li);
  367. });
  368. });
  369. },
  370. numform(text) {
  371. let num = text;
  372. if (isNaN(text)) num = '0';
  373. if (text >= 100000000) num = (text / 100000000).toFixed(2) - 0 + '亿';
  374. else if (text >= 10000) num = (text / 10000).toFixed(2) - 0 + '万';
  375. else num = num + '';
  376. return num;
  377. },
  378. line1(li) {
  379. if (linechart) {
  380. linechart.destroy();
  381. linechart = null;
  382. }
  383. const that = this;
  384. // 传播量趋势
  385. linechart = new F2.Chart({
  386. id: 'container',
  387. pixelRatio: window.devicePixelRatio
  388. });
  389. linechart.source(li, {
  390. dt: {
  391. tickCount: 5
  392. },
  393. playCount: {
  394. tickCount: 5
  395. }
  396. });
  397. linechart.axis('dt', {
  398. label: function label(text, index, total) {
  399. const textCfg = {};
  400. if (index === 0) {
  401. textCfg.textAlign = 'left';
  402. } else if (index === total - 1) {
  403. textCfg.textAlign = 'right';
  404. }
  405. return textCfg;
  406. }
  407. });
  408. linechart.scale('playCount', {
  409. formatter(text) {
  410. return that.numform(text);
  411. }
  412. });
  413. linechart.tooltip({
  414. triggerOn: ['click'],
  415. onShow: function onShow(ev) {
  416. const items = ev.items;
  417. items[0].name = items[0].title;
  418. items[1].name = null;
  419. items[1].value = 0;
  420. }
  421. });
  422. linechart.area().position('dt*playCount').color('l(90) 0:#1890FF 1:#f7f7f7').shape('smooth');
  423. linechart
  424. .line()
  425. .position('dt*playCount')
  426. .color('#1890FF')
  427. .style({
  428. lineWidth: 1
  429. })
  430. .shape('smooth');
  431. linechart.render();
  432. },
  433. bar1(li) {
  434. if (barchart) {
  435. barchart.destroy();
  436. barchart = null;
  437. }
  438. const that = this;
  439. barchart = new F2.Chart({
  440. id: 'barChart',
  441. pixelRatio: window.devicePixelRatio,
  442. padding: [10, 50, 30, 140],
  443. height: this.heightBar
  444. });
  445. barchart.source(li, {
  446. readCount: {
  447. tickCount: 3
  448. }
  449. });
  450. barchart.axis('readCount', {
  451. label: function label(text) {
  452. const textCfg = {};
  453. textCfg.text = that.numform(text);
  454. textCfg.fontSize = 12;
  455. return textCfg;
  456. },
  457. grid: null
  458. });
  459. barchart.axis('depName', {
  460. label: function label(text) {
  461. const textCfg = {};
  462. textCfg.text = text;
  463. textCfg.fontSize = 14;
  464. return textCfg;
  465. },
  466. grid: null
  467. });
  468. barchart.coord({
  469. transposed: true
  470. });
  471. barchart.tooltip(false);
  472. barchart.interval().position('depName*readCount').color('l(180) 0:#1890ff 1:#70cdd0'); // 定义柱状图渐变色
  473. barchart.render();
  474. // 绘制柱状图文本
  475. const offset = -5;
  476. const canvas = barchart.get('canvas');
  477. const group = canvas.addGroup();
  478. const shapes = {};
  479. li.forEach(function (obj) {
  480. const point = barchart.getPosition(obj);
  481. const text = group.addShape('text', {
  482. attrs: {
  483. x: point.x + 30,
  484. y: point.y + offset + 13,
  485. text: that.numform(obj.readCount),
  486. textAlign: 'center',
  487. textBaseline: 'bottom',
  488. fill: '#808080'
  489. }
  490. });
  491. shapes[obj.year] = text; // 缓存该 shape, 便于后续查找
  492. });
  493. },
  494. pie1(li) {
  495. if (piechart) {
  496. piechart.destroy();
  497. piechart = null;
  498. }
  499. const that = this;
  500. piechart = new F2.Chart({
  501. id: 'piechart',
  502. pixelRatio: window.devicePixelRatio
  503. });
  504. let lis = [];
  505. for (let i = 0; i < li.length; i++) {
  506. const v = li[i];
  507. if (!v.val || v.val <= 0) continue;
  508. lis.push(v);
  509. }
  510. piechart.source(lis);
  511. piechart.tooltip(false);
  512. piechart.legend(false);
  513. piechart.coord('polar', {
  514. transposed: true,
  515. innerRadius: 0.7,
  516. radius: 0.85
  517. });
  518. // 添加饼图文本
  519. piechart.pieLabel({
  520. anchorOffset: -10,
  521. label1: function label1(data, color) {
  522. return {
  523. text: data.p,
  524. fill: color
  525. };
  526. },
  527. label2: function label2(data) {
  528. return {
  529. text: ((data.val / that.playT) * 100).toFixed(2) + '%',
  530. fill: '#808080',
  531. fontWeight: 'bold'
  532. };
  533. }
  534. });
  535. piechart.axis(false);
  536. piechart
  537. .interval()
  538. .position('a*val')
  539. .color('p', [
  540. '#1890FF',
  541. '#13C2C2',
  542. '#2FC25B',
  543. '#FACC14',
  544. '#F04864',
  545. '#8543E0',
  546. '#3436C7',
  547. '#223273'
  548. ])
  549. .adjust('stack');
  550. piechart.render();
  551. },
  552. pagechange(p) {
  553. this.page = p;
  554. this.$nextTick(() => {
  555. this.grtzhData();
  556. });
  557. }
  558. },
  559. beforeDestroy() {
  560. linechart && linechart.destroy();
  561. barchart && barchart.destroy();
  562. piechart && piechart.destroy();
  563. linechart = null;
  564. barchart = null;
  565. piechart = null;
  566. },
  567. components: {}
  568. };
  569. </script>
  570. <style>
  571. .maincomponents .card {
  572. border-radius: 5px;
  573. background: #fff;
  574. padding: 8px 0 11px 0;
  575. margin-bottom: 5px;
  576. }
  577. .maincomponents .cardtit {
  578. border-left: 3px solid #1a89b3;
  579. padding-left: 5px;
  580. height: 25px;
  581. line-height: 25px;
  582. }
  583. .maincomponents .el-card__body {
  584. padding: 20px 0;
  585. }
  586. .maincomponents .numTitle {
  587. font-size: 24px;
  588. height: 1em;
  589. line-height: 1em;
  590. color: #1a89b3;
  591. font-weight: bold;
  592. }
  593. .maincomponents .numValue {
  594. margin-top: 8px;
  595. font-size: 14px;
  596. color: #919191;
  597. }
  598. .maincomponents .pl {
  599. width: 100%;
  600. }
  601. .maincomponents .platform {
  602. display: inline-block;
  603. text-align: center;
  604. width: 25%;
  605. padding-bottom: 0.5em;
  606. position: relative;
  607. }
  608. .maincomponents .line {
  609. border-right: 1px solid #eaeaea;
  610. width: 1px;
  611. height: 30px;
  612. top: 15px;
  613. position: absolute;
  614. }
  615. .maincomponents .el-table {
  616. color: #3b3b3b;
  617. }
  618. .maincomponents .el-table__header-wrapper {
  619. border-radius: 5px;
  620. overflow: hidden;
  621. }
  622. .maincomponents .has-gutter th {
  623. background: #eceff5;
  624. color: #3b3b3b;
  625. }
  626. </style>