index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. <template>
  2. <div class="live" style="padding-bottom: 1em">
  3. <table class="local_table">
  4. <tr>
  5. <td
  6. v-if="baseData.peopel_time !== -1"
  7. @click="() => showDetail('peopel_time')"
  8. >
  9. 直播观看人次
  10. <!-- <div class="showD">?</div> -->
  11. </td>
  12. <td v-if="baseData.peopel_time !== -1">
  13. {{ baseData.peopel_time | formatnum }}
  14. </td>
  15. <td
  16. v-if="baseData.msg_count !== -1"
  17. @click="() => showDetail('msg_count')"
  18. >
  19. 直播评论数
  20. <!-- <div class="showD">?</div> -->
  21. </td>
  22. <td v-if="baseData.msg_count !== -1">
  23. {{ baseData.msg_count | formatnum }}
  24. </td>
  25. </tr>
  26. <tr>
  27. <td
  28. v-if="baseData.avg_user_count !== -1"
  29. @click="() => showDetail('avg_user_count')"
  30. >
  31. 平均在线人数
  32. <!-- <div class="showD">?</div> -->
  33. </td>
  34. <td v-if="baseData.avg_user_count !== -1">
  35. {{ baseData.avg_user_count | formatnum }}
  36. </td>
  37. <td
  38. v-if="baseData.total_fans_count !== -1"
  39. @click="() => showDetail('total_fans_count')"
  40. >
  41. 新增粉丝数
  42. <!-- <div class="showD">?</div> -->
  43. </td>
  44. <td v-if="baseData.total_fans_count !== -1">
  45. {{ baseData.total_fans_count | formatnum }}
  46. </td>
  47. </tr>
  48. <tr>
  49. <td
  50. v-if="baseData.turn_rate !== -1"
  51. @click="() => showDetail('turn_rate')"
  52. >
  53. 转粉率
  54. <!-- <div class="showD">?</div> -->
  55. </td>
  56. <td v-if="baseData.turn_rate !== -1">
  57. {{ baseData.turn_rate | formatnum }}
  58. </td>
  59. <td
  60. v-if="baseData.total_fans_count !== -1"
  61. @click="() => showDetail('total_fans_count')"
  62. >
  63. 最大在线人数
  64. <!-- <div class="showD">?</div> -->
  65. </td>
  66. <td v-if="baseData.total_fans_count !== -1">
  67. {{ baseData.total_fans_count | formatnum }}
  68. </td>
  69. </tr>
  70. </table>
  71. <div v-if="gender.length" class="title">观众性别分布</div>
  72. <canvas
  73. v-if="gender.length"
  74. id="audienceSex"
  75. class="canvas"
  76. :width="client.width"
  77. :height="client.height"
  78. ></canvas>
  79. <div class="title">观众地域分布</div>
  80. <div
  81. id="audienceRegion"
  82. ref="audienceRegion"
  83. class="canvas"
  84. :style="{width: client.width + 'px', height: client.height + 'px'}"
  85. ></div>
  86. <!-- <canvas
  87. id="audienceRegion"
  88. ref="audienceRegion"
  89. class="canvas"
  90. :width="client.width"
  91. :height="client.height"
  92. ></canvas> -->
  93. <div v-if="RealTimeTraffic.peopleList.length" class="title">实时流量</div>
  94. <canvas
  95. v-if="RealTimeTraffic.peopleList.length"
  96. id="RealTimeTraffic"
  97. class="canvas"
  98. :width="client.width"
  99. :height="client.height"
  100. ></canvas>
  101. <div v-if="RealTimePersonTime.peopleList.length" class="title">
  102. 观看人次
  103. </div>
  104. <canvas
  105. v-if="RealTimePersonTime.peopleList.length"
  106. id="RealTimePersonTime"
  107. class="canvas"
  108. :width="client.width"
  109. :height="client.height"
  110. ></canvas>
  111. <div v-if="LikeTheTrend.peopleList.length" class="title">点赞走势</div>
  112. <canvas
  113. v-if="LikeTheTrend.peopleList.length"
  114. id="LikeTheTrend"
  115. class="canvas"
  116. :width="client.width"
  117. :height="client.height"
  118. ></canvas>
  119. </div>
  120. </template>
  121. <style scoped>
  122. .live {
  123. height: 100%;
  124. overflow-y: scroll;
  125. }
  126. .showD {
  127. display: inline-block;
  128. width: 1.1em;
  129. height: 1.1em;
  130. text-align: center;
  131. line-height: 1.15em;
  132. border-radius: 50%;
  133. border: 1px solid #1989fa;
  134. color: #1989fa;
  135. display: none;
  136. }
  137. </style>
  138. <script>
  139. import { getlive } from "@/api/getList.js";
  140. import filters from "@/utils/filters/index";
  141. import F2 from "@antv/f2/lib/index-all";
  142. // import DataSet from "@antv/data-set";
  143. import mapJSON from "./mapJSON.js";
  144. import { Dialog } from "vant";
  145. import "vant/lib/dialog/style";
  146. import * as echarts from "echarts";
  147. export default {
  148. components: {},
  149. props: {
  150. title: {
  151. type: String,
  152. value: "",
  153. },
  154. season: {
  155. type: String,
  156. value: 1,
  157. },
  158. },
  159. data() {
  160. return {
  161. colors: {},
  162. initCanvas: {
  163. audienceSex: undefined,
  164. account: undefined,
  165. },
  166. baseData: {},
  167. client: {
  168. width: 0,
  169. height: 0,
  170. },
  171. gender: [],
  172. RealTimeTraffic: { title: "实时流量", peopleList: [] },
  173. RealTimePersonTime: { title: "观看人次", peopleList: [] },
  174. LikeTheTrend: { title: "点赞走势", peopleList: [] },
  175. };
  176. },
  177. created() {
  178. let title = decodeURIComponent(this.title);
  179. let clientWidth = document.body.clientWidth || 0;
  180. this.client = {
  181. width: clientWidth,
  182. height: (clientWidth / 16) * 9,
  183. };
  184. getlive({
  185. topic: title,
  186. platform: "douyin",
  187. season: this.season,
  188. }).then(res => {
  189. let oriData = res || {};
  190. let trend = oriData.trend || {};
  191. let RealTimeTraffic = {
  192. title: "实时流量",
  193. peopleList: [],
  194. };
  195. let RealTimePersonTime = {
  196. title: "观看人次",
  197. peopleList: [],
  198. };
  199. let LikeTheTrend = {
  200. title: "点赞走势",
  201. peopleList: [],
  202. };
  203. // 趋势图数据
  204. for (let i = 0; i < (trend.webcastTrendList || []).length; i++) {
  205. const v = (trend.webcastTrendList || [])[i];
  206. let time = v.crawl_date.split(" ")[1] || "";
  207. v.user_count !== undefined &&
  208. RealTimeTraffic.peopleList.push({
  209. value: v.user_count || 0,
  210. time: time,
  211. });
  212. v.stats_total_user !== undefined &&
  213. RealTimePersonTime.peopleList.push({
  214. value: v.stats_total_user || 0,
  215. time: time,
  216. });
  217. v.like_count !== undefined &&
  218. LikeTheTrend.peopleList.push({
  219. value: v.like_count || 0,
  220. time: time,
  221. });
  222. }
  223. // 基础数据
  224. let core_date = oriData.core_data || {};
  225. this.baseData = {
  226. msg_count: oriData.webcastMessageList
  227. ? oriData.webcastMessageList.count || 0
  228. : 0,
  229. like_count: core_date.like_count || 0,
  230. total_fans_count: core_date.add_fans_count || 0,
  231. peopel_time: (trend.user || {}).stats_total_user || 0,
  232. avg_user_count: core_date.avg_user_count || 0,
  233. max_user_count: core_date.max_user_count || 0,
  234. turn_rate: (core_date.turn_rate || 0) * 100,
  235. };
  236. // 来源城市
  237. let city = oriData.city || [];
  238. for (let i = 0; i < city.length; i++) {
  239. const element = city[i] || {};
  240. for (let o = 0; o < mapJSON.features.length; o++) {
  241. const item = mapJSON.features[o];
  242. if (item.properties.name === element.key) {
  243. item.properties.value = ((element.rate * 100).toFixed(1) - 0);
  244. break;
  245. }
  246. }
  247. }
  248. // 性别比
  249. let gender = [];
  250. let oriGender = oriData.gender || [];
  251. for (let i = 0; i < oriGender.length; i++) {
  252. const v = oriGender[i];
  253. gender.push({
  254. name: v.key == "1" ? "男" : "女",
  255. percent: v.count,
  256. a: "1",
  257. });
  258. }
  259. this.gender = gender;
  260. this.RealTimeTraffic = RealTimeTraffic;
  261. this.RealTimePersonTime = RealTimePersonTime;
  262. this.LikeTheTrend = LikeTheTrend;
  263. this.$nextTick(() => {
  264. this.cake(gender);
  265. RealTimeTraffic.peopleList.length &&
  266. this.line(RealTimeTraffic.peopleList, "RealTimeTraffic");
  267. RealTimePersonTime.peopleList.length &&
  268. this.line(RealTimePersonTime.peopleList, "RealTimePersonTime");
  269. LikeTheTrend.peopleList.length &&
  270. this.line(LikeTheTrend.peopleList, "LikeTheTrend");
  271. this.map();
  272. });
  273. });
  274. },
  275. methods: {
  276. cake(list) {
  277. const chart = new F2.Chart({
  278. id: "audienceSex",
  279. pixelRatio: window.devicePixelRatio,
  280. });
  281. chart.source(list, {
  282. percent: {
  283. formatter: function formatter(val) {
  284. return val * 100 + "%";
  285. },
  286. },
  287. });
  288. chart.tooltip(false);
  289. chart.coord("polar", {
  290. transposed: true,
  291. radius: 0.75,
  292. });
  293. chart.legend(false);
  294. chart.axis(false);
  295. // 添加饼图文本
  296. chart.pieLabel({
  297. sidePadding: 40,
  298. label1: function label1(data, color) {
  299. return {
  300. text: data.name + ":" + filters.formatNum(data.percent) + "人",
  301. fill: color,
  302. };
  303. },
  304. });
  305. chart
  306. .interval()
  307. .position("a*percent")
  308. .color("name", ["#13C2C2", "#2FC25B", "#FACC14", "#F04864", "#8543E0"])
  309. .adjust("stack")
  310. .style({
  311. lineWidth: 1,
  312. stroke: "#fff",
  313. lineJoin: "round",
  314. lineCap: "round",
  315. })
  316. .animate({
  317. appear: {
  318. duration: 1200,
  319. easing: "bounceOut",
  320. },
  321. });
  322. chart.render();
  323. this.initCanvas.audienceSex = chart;
  324. },
  325. line(list, id) {
  326. const chart = new F2.Chart({
  327. id,
  328. pixelRatio: window.devicePixelRatio,
  329. });
  330. chart.source(list, {
  331. time: {
  332. tickCount: 3,
  333. range: [0, 1],
  334. },
  335. value: {
  336. tickCount: 5,
  337. min: 0,
  338. },
  339. });
  340. chart.axis("time", {
  341. label: function label(text, index, total) {
  342. const textCfg = {};
  343. if (index === 0) {
  344. textCfg.textAlign = "left";
  345. } else if (index === total - 1) {
  346. textCfg.textAlign = "right";
  347. }
  348. return textCfg;
  349. },
  350. });
  351. chart.axis("value", {
  352. label: function label(text) {
  353. return { text: filters.formatNum(text) };
  354. },
  355. });
  356. chart.tooltip({
  357. showTitle: true,
  358. });
  359. chart
  360. .area()
  361. .position("time*value")
  362. .color("l(90) 0:#1890FF 1:#f7f7f7")
  363. .shape("smooth");
  364. chart
  365. .line()
  366. .position("time*value")
  367. .color("#1890FF")
  368. .shape("smooth")
  369. .size(0.5);
  370. chart.render();
  371. },
  372. map() {
  373. let myChart = echarts.init(this.$refs.audienceRegion);
  374. window.onresize = myChart.resize;
  375. echarts.registerMap("china", mapJSON);
  376. let data = mapJSON.features.map(v => {
  377. return {
  378. name: v.properties.name,
  379. value: v.properties.value,
  380. };
  381. });
  382. data = data.sort((a, b) => {
  383. return b.value - a.value;
  384. });
  385. myChart.setOption({
  386. tooltip: {
  387. show: true,
  388. triggerOn: "click",
  389. formatter: "{c}%",
  390. },
  391. visualMap: {
  392. show: false,
  393. min: 0,
  394. max: data[0].value,
  395. inRange: {
  396. color: ["rgba(0,0,0,.0)", "yellow"],
  397. },
  398. },
  399. series: [
  400. {
  401. type: "map",
  402. mapType: "china",
  403. geoIndex: 0,
  404. zoom: 1.25,
  405. label: {
  406. show: false,
  407. },
  408. itemStyle: {
  409. normal: {
  410. borderColor: "#76acff", //每个区域的边框色
  411. areaColor: "rgba(118,172,255, 0)", //区域背景色
  412. },
  413. },
  414. data,
  415. },
  416. ],
  417. });
  418. },
  419. showDetail(key) {
  420. let val = "",
  421. nameText = "";
  422. let list = this.$parent.$parent.$parent.$parent.config || [];
  423. for (let i = 0; i < list.length; i++) {
  424. const v = list[i];
  425. if (key !== v.name || v.topic_id !== this.baseData.topic_id) continue;
  426. val = v.value;
  427. nameText = v.content_desc;
  428. break;
  429. }
  430. if (!val) return;
  431. Dialog.alert({
  432. confirmButtonText: "关闭",
  433. title: this.$route.query.title + "-" + nameText,
  434. message: val,
  435. confirmButtonColor: "#1989fa",
  436. messageAlign: "left",
  437. theme: "round-button",
  438. });
  439. },
  440. },
  441. };
  442. </script>