video.vue 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. <template>
  2. <div class="video" ref="bg">
  3. <video
  4. ref="video"
  5. :controls="false"
  6. autoplay
  7. @timeupdate="timeupdate"
  8. @error="videoerror"
  9. @canplay="videoload"
  10. :src="props.item.video_url"
  11. ></video>
  12. <div style="position: relative">
  13. <div ref="chartEle"></div>
  14. <div class="lineinfobackground" :style="'left:' + leftNum + 'px'">
  15. <div style="position: relative; width: 100%; height: 100%">
  16. <div class="line"></div>
  17. <el-row style="padding: 10px 0 0 10px">
  18. <el-col :span="6">
  19. <span class="icon" style="background-color: #e05c5c"></span>
  20. 流入
  21. </el-col>
  22. <el-col :span="6">
  23. <span class="icon" style="background-color: #6ab581"></span>
  24. 流出
  25. </el-col>
  26. <el-col :span="12">
  27. <span class="iconLine"></span>
  28. 收视率%
  29. <div style="float: right; padding: 0 5px 0 0">
  30. {{ tof(jindu.audienceRating, 4) }}%
  31. </div>
  32. </el-col>
  33. </el-row>
  34. <div style="padding: 5px 10px">
  35. 当前时间:{{ jindu.date ? jindu.date.split(' ')[1] || '' : '' }}
  36. <div style="float: right">人数: {{ formateNum(jindu.uv) }}</div>
  37. </div>
  38. <el-row>
  39. <el-col :span="12">
  40. <div style="padding: 0 10px">
  41. 流入排行
  42. <div>
  43. <div
  44. style="font-size: 12px; line-height: 22px"
  45. v-for="(item, i) in jindu.lrTop"
  46. :key="i"
  47. v-text="item"
  48. ></div>
  49. </div>
  50. </div>
  51. </el-col>
  52. <el-col :span="12">
  53. <div style="padding: 0 10px">
  54. 流出排行
  55. <div>
  56. <div
  57. style="font-size: 12px; line-height: 22px"
  58. v-for="(item, i) in jindu.lcTop"
  59. :key="i"
  60. v-text="item"
  61. ></div>
  62. </div>
  63. </div>
  64. </el-col>
  65. </el-row>
  66. </div>
  67. </div>
  68. </div>
  69. </div>
  70. </template>
  71. <script setup>
  72. // @ is an alias to /src
  73. import { defaultAjax } from '@/api/index';
  74. import { ref, defineProps, onMounted } from 'vue';
  75. import * as echarts from 'echarts';
  76. const video = ref(null);
  77. const bg = ref(null);
  78. const leftNum = ref(100);
  79. const chartEle = ref(null);
  80. const props = defineProps({
  81. item: Object,
  82. });
  83. const jindu = ref({});
  84. let chart = undefined;
  85. let timeout = undefined;
  86. let isClick = false;
  87. let list = [];
  88. let sec = 0,
  89. stepNum = 0;
  90. onMounted(() => {
  91. chart = echarts.init(chartEle.value);
  92. chart.resize({
  93. height: 300,
  94. width: bg.value.offsetWidth,
  95. });
  96. chart.on('datazoom', () => {
  97. if (isClick) return;
  98. isClick = true;
  99. if (timeout) clearTimeout(timeout), (timeout = undefined);
  100. video.value.pause();
  101. timeout = setTimeout(() => {
  102. clearTimeout(timeout);
  103. timeout = undefined;
  104. isClick = false;
  105. // 拖拽后的事件
  106. video.value.currentTime = chart.getOption().dataZoom[0].startValue;
  107. stepNum = chart.getOption().dataZoom[0].endValue - chart.getOption().dataZoom[0].startValue
  108. video.value.play();
  109. }, 200);
  110. });
  111. });
  112. defaultAjax({
  113. url: props.item.flow_url,
  114. }).then(r => {
  115. const li = r || {};
  116. const Ir = [],
  117. Ic = [],
  118. ad = [],
  119. keys = [];
  120. for (const key in li) {
  121. if (!Object.hasOwnProperty.call(li, key)) continue;
  122. const v = li[key];
  123. keys.push(key);
  124. Ic.push(-1 * v.lc);
  125. Ir.push(v.lr);
  126. ad.push(v.audienceRating * 100);
  127. v.date = key;
  128. list.push(v);
  129. }
  130. stepNum = list.length;
  131. draw(keys, Ir, Ic, ad);
  132. jindu.value = list[0];
  133. });
  134. function timeupdate(v) {
  135. // 播放中
  136. if (sec === Math.floor(v.srcElement.currentTime)) return;
  137. sec = Math.floor(v.srcElement.currentTime);
  138. let s = chart.getOption().dataZoom[0].startValue,
  139. e = chart.getOption().dataZoom[0].endValue;
  140. let item = list[sec] || {};
  141. jindu.value = item;
  142. if (sec >= e) {
  143. // 切页
  144. chart.setOption({
  145. dataZoom: {
  146. startValue: e + 1,
  147. endValue: e + 1 + e - s
  148. },
  149. });
  150. leftNum.value = 100;
  151. return;
  152. }
  153. let width = bg.value.offsetWidth - 130;
  154. let cValue = sec - s;
  155. let iWdith = width / (e - s);
  156. leftNum.value = cValue * iWdith + 100;
  157. }
  158. function videoerror(err) {
  159. console.log('播放错误:', err);
  160. }
  161. function videoload() {
  162. // 可以播放
  163. console.log('可以播放', video.value);
  164. }
  165. function formateNum(n) {
  166. let num = n;
  167. if (isNaN(n)) num = '0';
  168. else if (n >= 100000000)
  169. num =
  170. ((n / 100000000).toFixed(2) - 0 + '').replace(
  171. /\B(?=(?:\d{3})+\b)/g,
  172. ','
  173. ) + '亿';
  174. else if (n >= 10000)
  175. num =
  176. ((n / 10000).toFixed(2) - 0 + '').replace(/\B(?=(?:\d{3})+\b)/g, ',') +
  177. '万';
  178. else num = (num + '').replace(/\B(?=(?:\d{3})+\b)/g, ',');
  179. return num;
  180. }
  181. function tof(num, t) {
  182. return Number(num * 100).toFixed(t || 2);
  183. }
  184. function draw(key, Ir, Ic, ad) {
  185. // 绘图
  186. let option = {
  187. dataZoom: [
  188. {
  189. maxSpan: 100,
  190. minSpan: 1,
  191. type: 'slider',
  192. brushSelect: false,
  193. // zoomLock: false,
  194. show: true,
  195. realtime: true,
  196. startValue: 0,
  197. endValue: stepNum,
  198. zoomOnMouseWheel: false,
  199. height: '30',
  200. top: '85%',
  201. handleIcon:
  202. 'path://M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
  203. handleSize: 15,
  204. textStyle: {
  205. color: '#c5c6cc',
  206. },
  207. dataBackground: {
  208. lineStyle: {
  209. color: '#fff',
  210. },
  211. areaStyle: {
  212. color: '#8b51a8',
  213. },
  214. },
  215. },
  216. ],
  217. xAxis: {
  218. data: key,
  219. position: 'top',
  220. silent: false,
  221. axisLabel: {
  222. interval: 'auto',
  223. color: '#000',
  224. rotate: '-90',
  225. },
  226. },
  227. yAxis: [
  228. {
  229. scale: false,
  230. nameGap: 6,
  231. inverse: false,
  232. name: '流入流出',
  233. nameLocation: 'start',
  234. splitArea: {
  235. show: false,
  236. },
  237. splitLine: {
  238. show: false,
  239. },
  240. axisLabel: {
  241. color: '#000',
  242. formatter: function (value) {
  243. let v = value;
  244. if (v < 0) v *= -1;
  245. return v;
  246. },
  247. },
  248. axisTick: {
  249. show: false,
  250. },
  251. axisLine: {
  252. show: false,
  253. },
  254. nameTextStyle: {
  255. color: '#000',
  256. },
  257. },
  258. {
  259. scale: true,
  260. nameGap: 6,
  261. type: 'value',
  262. name: '收视率%',
  263. nameLocation: 'start',
  264. splitLine: {
  265. show: false,
  266. },
  267. axisTick: {
  268. show: false,
  269. },
  270. axisLine: {
  271. show: false,
  272. },
  273. axisLabel: {
  274. color: '#000',
  275. },
  276. nameTextStyle: {
  277. color: '#000',
  278. },
  279. },
  280. ],
  281. grid: {
  282. left: 100,
  283. right: 50,
  284. },
  285. series: [
  286. {
  287. name: '流入',
  288. type: 'bar',
  289. yAxisIndex: 0,
  290. stack: 'one',
  291. data: Ir,
  292. sampling: 'lttb',
  293. emphasis: {
  294. focus: 'series',
  295. },
  296. itemStyle: {
  297. color: '#df5a5a',
  298. },
  299. },
  300. {
  301. name: '流出',
  302. type: 'bar',
  303. yAxisIndex: 0,
  304. sampling: 'lttb',
  305. stack: 'one',
  306. data: Ic,
  307. emphasis: {
  308. focus: 'series',
  309. },
  310. itemStyle: {
  311. color: '#6ab581',
  312. },
  313. },
  314. {
  315. name: '收视率%',
  316. type: 'line',
  317. yAxisIndex: 1,
  318. smooth: true,
  319. showSymbol: false,
  320. data: ad,
  321. lineStyle: {
  322. width: 1,
  323. color: '#50abfd',
  324. },
  325. },
  326. ],
  327. };
  328. option && chart.setOption(option);
  329. }
  330. </script>
  331. <style lang="scss">
  332. .video {
  333. padding: 0.5em;
  334. video {
  335. width: 100%;
  336. max-width: 720px;
  337. margin: 0 auto;
  338. display: block;
  339. }
  340. .lineinfobackground {
  341. font-size: 12px;
  342. border-radius: 9px;
  343. position: absolute;
  344. width: 305px;
  345. height: 200px;
  346. top: 30px;
  347. left: 50px;
  348. background: rgba(0, 0, 0, 0.5);
  349. transform: translateX(-50%);
  350. color: #fff;
  351. .line {
  352. position: absolute;
  353. width: 2px;
  354. height: 110%;
  355. margin-top: -5%;
  356. margin-left: 50%;
  357. background-color: #888;
  358. }
  359. .icon {
  360. display: inline-block;
  361. width: 30px;
  362. height: 10px;
  363. border-radius: 10px;
  364. }
  365. .iconLine {
  366. background-color: #50abfd;
  367. display: inline-block;
  368. width: 30px;
  369. height: 2px;
  370. vertical-align: middle;
  371. border-radius: 10px;
  372. }
  373. }
  374. }
  375. </style>