transaction-data.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. <?php
  2. use common\helpers\ComponentHelper;
  3. ComponentHelper::loadComponentView('components/my-table', __DIR__);
  4. ComponentHelper::loadComponentView('components/screen-contain', __DIR__);
  5. ComponentHelper::loadComponentView('components/count-to', __DIR__);
  6. ComponentHelper::loadComponentView('components/header-time', __DIR__);
  7. ?>
  8. <style>
  9. .screen-container {
  10. min-width: 100%;
  11. }
  12. .bgCom {
  13. border: 2px solid #0061CC;
  14. margin-bottom: 16px;
  15. background: linear-gradient(135deg, #0034E4 0%, #14141A 13%, rgba(7, 7, 9, 0.89) 81%, rgba(20, 101, 255, 0.11) 100%);
  16. border-radius: 16px;
  17. padding: 30px;
  18. }
  19. .titleCom {
  20. height: 37px;
  21. font-size: 30px;
  22. font-family: DIN;
  23. font-weight: bold;
  24. line-height: 37px;
  25. color: #F8F8F8;
  26. margin-bottom: 30px;
  27. }
  28. .subTitle {
  29. line-height: 37px;
  30. text-align: center;
  31. }
  32. .transaction-data-box {
  33. padding: 30px 60px;
  34. }
  35. .transaction-data-header {
  36. display: flex;
  37. justify-content: center;
  38. align-items: center;
  39. height: 80px;
  40. color: #F8F8F8;
  41. padding-right: 460px;
  42. }
  43. .count-symbool {
  44. height: 100%;
  45. padding-left: 7px;
  46. font-size: 18px;
  47. /* font-weight: 700; */
  48. }
  49. .count-to {
  50. font-size: 60px;
  51. }
  52. /* left */
  53. .transaction-data-left{
  54. margin: 0 16px;
  55. }
  56. .transaction-item {
  57. width: 100%;
  58. height: 192px;
  59. }
  60. .transaction-item-child {
  61. position: relative;
  62. width: 20%;
  63. }
  64. .child-count {
  65. padding: 10px 0 2px;
  66. font-size: 20px;
  67. }
  68. .child-tag {
  69. position: absolute;
  70. top: 0;
  71. right: 20px;
  72. /* width: 30px; */
  73. padding: 2px 5px;
  74. background: #0034E4;
  75. opacity: 1;
  76. border-radius: 8px 8px 8px 0px;
  77. font-size: 12px;
  78. text-align: center;
  79. }
  80. .transaction-item-kids {
  81. width: 152px;
  82. height: 152px;
  83. background: radial-gradient(circle, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.08) 100%);
  84. opacity: 1;
  85. border-radius: 12px;
  86. overflow: hidden;
  87. }
  88. .transaction-item-kids .child-count {
  89. padding-top: 0;
  90. }
  91. .transaction-category-left{
  92. width: 55%;
  93. height: 400px;
  94. margin-right: 28px;
  95. padding: 74px 36px;
  96. position: relative;
  97. }
  98. .transaction-category-right{
  99. width: 45%;
  100. height: 400px;
  101. }
  102. #echartsTrend {
  103. width: 100%;
  104. height: 300px;
  105. }
  106. /* right */
  107. .transaction-data-right {
  108. padding: 0 16px;
  109. }
  110. .transaction-block {
  111. width: 100%;
  112. }
  113. .block-items {
  114. position: relative;
  115. flex: 1;
  116. height: 498px;
  117. }
  118. .block-map-tips {
  119. position: absolute;
  120. bottom: 20px;
  121. left: 30px;
  122. }
  123. .map-tips-color {
  124. width: 12px;
  125. height: 12px;
  126. border-radius: 50%;
  127. }
  128. .map-tips-name {
  129. padding-left: 5px;
  130. }
  131. .transaction-sales {
  132. width: 100%;
  133. height: 302px;
  134. }
  135. .table-goods {
  136. cursor: pointer;
  137. }
  138. .goods-name {
  139. color: #F8F8F8;
  140. padding-left: 6px;
  141. }
  142. #echartsMap {
  143. width: 100%;
  144. height: 388px;
  145. }
  146. .category-left-model{
  147. height: 85px;
  148. margin-bottom: 4px;
  149. font-size: 16px;
  150. line-height: 26px;
  151. color: #FFF;
  152. position: relative;
  153. width: 70%;
  154. }
  155. .category-left-bak{
  156. width: 90%;
  157. padding: 18px 20px;
  158. }
  159. .left-text{
  160. width: 33%;
  161. }
  162. .right-text{
  163. position: absolute;
  164. right: 0;
  165. height: 100%;
  166. z-index: 2;
  167. width: 121px;
  168. /* 实现梯形 */
  169. border-left: 20px solid transparent;
  170. border-right: 20px solid transparent;
  171. }
  172. .right-text-model{
  173. position: absolute;
  174. left: 50%;
  175. transform: translateX(-50%);
  176. z-index: 3;
  177. }
  178. .right-text-model-center{
  179. top: 50%;
  180. transform: translate(-50%,-50%);
  181. }
  182. .right-border{
  183. transform: skewX(342deg);
  184. position: absolute;
  185. border: 1px solid #303363;
  186. border-left: 0;
  187. right: -70px;
  188. bottom: -42px;
  189. height: 80px;
  190. width: 25%;
  191. z-index: 1;
  192. }
  193. .big-right-border{
  194. transform: skewX(342deg);
  195. position: absolute;
  196. border: 1px solid #303363;
  197. border-left: 0;
  198. right: 66px;
  199. top: 94px;
  200. height: 56%;
  201. width: 30%;
  202. z-index: 1;
  203. }
  204. .right-border-text{
  205. position: absolute;
  206. right: -90px;
  207. top: 50%;
  208. transform: translateY(-50%) skewX(18deg);
  209. }
  210. .el-carousel--vertical{
  211. width: 200px;
  212. height: 30px;
  213. }
  214. .el-carousel__indicators--vertical{
  215. display: none;
  216. }
  217. </style>
  218. <div id="app" v-cloak>
  219. <screen-contain text-left="交易数据" :is-fix="false">
  220. <template v-slot:right>
  221. <header-time></header-time>
  222. </template>
  223. <div class="transaction-data-header">
  224. <div style="font-size: 45px;margin-right: 18px;">平台累计成交金额</div>
  225. <count-to :end-val="totalNum" :duration="10000" class="count-to"></count-to>
  226. <div class="count-symbool" flex="cross:bottom"><span>元</span></div>
  227. </div>
  228. <div class="transaction-data-box">
  229. <div class="transaction-data-left" flex="cross:center">
  230. <div class="bgCom transaction-category-left">
  231. <div class="category-left-model" flex
  232. v-for="(item,index) in orderModelArr" :key="index"
  233. >
  234. <div class="category-left-bak" flex :style="{'background': item.color + '1c',}">
  235. <div v-for="(data,dIndex) in item.data" :key="dIndex" class="left-text">
  236. <div>{{data.text}}</div>
  237. <div>{{echartsData.from[data.key]}}</div>
  238. </div>
  239. </div>
  240. <div class="right-text">
  241. <div class="right-text-model right-text"
  242. :style="{
  243. 'width': orderModelMaxWidth - (index*40) + 'px',
  244. 'border-top': '85px solid ' + item.color,
  245. }"
  246. ></div>
  247. <div class="right-text-model right-text-model-center">{{item.text}}</div>
  248. </div>
  249. <!-- 线条 -->
  250. <div class="right-border" v-if="index != orderModelArr.length - 1">
  251. <div class="right-border-text" :style="{'right': -100 - (index * 30) + 'px'}">
  252. <div>{{item.borderInfo.text}}</div>
  253. <div>{{echartsData.from[item.borderInfo.key]}}%</div>
  254. </div>
  255. </div>
  256. </div>
  257. <div class="big-right-border"></div>
  258. </div>
  259. <div class="bgCom transaction-category-right">
  260. <div flex="main:justify">
  261. <div class="titleCom" style="margin-bottom: 0;">实时成交趋势</div>
  262. <div class="subTitle">单位:元 </div>
  263. </div>
  264. <div id="echartsTrend"></div>
  265. </div>
  266. </div>
  267. <div class="transaction-data-right" flex="dir:top cross:center">
  268. <div class="transaction-block" flex="main:around">
  269. <div class="bgCom block-items">
  270. <div flex="main:justify">
  271. <div class="titleCom">交易地图热点</div>
  272. <el-carousel height="50px" direction="vertical">
  273. <el-carousel-item v-for="(item,index) in echartsData.scroll" :key="index">
  274. <div class="subTitle">{{item}}</div>
  275. </el-carousel-item>
  276. </el-carousel>
  277. </div>
  278. <div id="echartsMap"></div>
  279. <div class="block-map-tips">
  280. <div v-for="(it, i) in mapTips" :key="i" flex="cross:center" style="padding-bottom: 5px;">
  281. <div class="map-tips-color" :style="{background: it.color}"></div>
  282. <div class="map-tips-name">{{ it.name }}</div>
  283. </div>
  284. </div>
  285. </div>
  286. <div class="bgCom block-items" flex="dir:top" style="margin: 0 38px">
  287. <div class="titleCom">加购情况</div>
  288. <div style="flex:1;height:0">
  289. <my-table :data="echartsData.cart" :options="options">
  290. <template v-slot:sort="{ item,index }">
  291. <div>{{index+1}}</div>
  292. </template>
  293. </my-table>
  294. </div>
  295. </div>
  296. <div class="bgCom block-items" flex="dir:top">
  297. <div class="titleCom">销售排行榜</div>
  298. <div style="flex:1;height:0">
  299. <my-table :data="echartsData.rank" :options="goodsFlowOptions">
  300. <template v-slot:sort="{ item,index }">
  301. <div>{{index+1}}</div>
  302. </template>
  303. </my-table>
  304. </div>
  305. </div>
  306. </div>
  307. </div>
  308. </div>
  309. </screen-contain>
  310. </div>
  311. <!-- <script src="https://cdn.jsdelivr.net/npm/echarts@5.2.2/dist/echarts.min.js"></script> -->
  312. <script src="/resources/unpkg/echarts@5.2.2/dist/echarts.min.js"></script>
  313. <script src="/resources/js/china.js"></script>
  314. <script>
  315. function randomData() {
  316. return Math.round(Math.random() * 500);
  317. }
  318. const app = new Vue({
  319. el: '#app',
  320. data() {
  321. return {
  322. orderModelArr: [
  323. {
  324. text: '访客',
  325. color: '#0034E4',
  326. borderInfo: {
  327. text: '下单转化率',
  328. key: 'order_proportion'
  329. },
  330. data: [
  331. {
  332. text: '访客数',
  333. key: 'total_visitors',
  334. },
  335. ],
  336. },
  337. {
  338. text: '下单',
  339. color: '#1F9E34',
  340. borderInfo: {
  341. text: '下单支付转化率',
  342. key: 'pay_proportion'
  343. },
  344. data: [
  345. {
  346. text: '下单人数',
  347. key: 'number_of_orders',
  348. },
  349. {
  350. text: '下单金额',
  351. key: 'total_order_money',
  352. },
  353. ],
  354. },
  355. {
  356. text: '支付',
  357. color: '#ED7453',
  358. data: [
  359. {
  360. text: '支付人数',
  361. key: 'total_pay_people',
  362. },
  363. {
  364. text: '支付金额',
  365. key: 'total_pay_money',
  366. },
  367. {
  368. text: '客单价',
  369. key: 'unit_price',
  370. },
  371. ],
  372. },
  373. ],
  374. orderModelMaxWidth: 180,
  375. options: [{
  376. props: "sort",
  377. label: '排行',
  378. width: '50',
  379. slot: true,
  380. textAlign: 'left',
  381. },{
  382. props: 'goods_name',
  383. label: '商品名称',
  384. textAlign: 'left',
  385. width: '190'
  386. },{
  387. props: 'num',
  388. label: '加购数量',
  389. textAlign: 'right',
  390. },
  391. ],
  392. goodsFlowOptions: [{
  393. props: "sort",
  394. label: '排行',
  395. width: '50',
  396. slot: true,
  397. textAlign: 'left',
  398. },
  399. {
  400. props: 'goods_name',
  401. label: '商品名称',
  402. textAlign: 'left',
  403. width: '280',
  404. },
  405. {
  406. props: 'num',
  407. label: '销售金额',
  408. textAlign: 'right',
  409. },
  410. ],
  411. mapTips: [
  412. {
  413. color: '#ED7453',
  414. name: '≥100'
  415. },
  416. {
  417. color: '#A2E157',
  418. name: '>90-100'
  419. },
  420. {
  421. color: '#0034E4',
  422. name: '>80-90'
  423. },
  424. {
  425. color: '#002390',
  426. name: '<80'
  427. }
  428. ],
  429. echartsData: {
  430. from: {},
  431. scroll: [],
  432. },
  433. totalNum: 0,
  434. }
  435. },
  436. mounted() {
  437. this.getEchartsData();
  438. },
  439. methods: {
  440. getEchartsData(){
  441. let that = this;
  442. request({
  443. params: {
  444. r: 'mall/statistics/car-user-trad-data',
  445. },
  446. method: 'get'
  447. }).then(e => {
  448. if (e.data.code === 0) {
  449. console.info(e.data.data);
  450. this.echartsData = e.data.data;
  451. this.totalNum = this.echartsData.total_num;
  452. // this.totalCount = e.data.data.flow_datas.total_users;
  453. // this.orderSort.push(...e.data.data.region_datas.sort((a,b) => { return b.total_orders-a.total_orders}).slice(0,10));
  454. } else {
  455. this.$message.error(e.data.msg);
  456. }
  457. this.echartsTrendInit()
  458. this.echartsMapInit()
  459. });
  460. },
  461. echartsTrendInit() {
  462. var chartDom = document.getElementById('echartsTrend');
  463. var myChart = echarts.init(chartDom);
  464. // 指定图表的配置项和数据
  465. var option = {
  466. xAxis: {
  467. type: 'category',
  468. data: this.echartsData.trad_trend.map(v => v.hour)
  469. },
  470. yAxis: {
  471. type: 'value'
  472. },
  473. tooltip: {
  474. trigger: "axis",
  475. },
  476. series: [
  477. {
  478. data: this.echartsData.trad_trend.map(v => v.num),
  479. type: 'line',
  480. color: "#0034E4",
  481. areaStyle: {
  482. opacity: 0.5
  483. }
  484. }
  485. ]
  486. };
  487. myChart.setOption(option);
  488. },
  489. echartsMapInit() {
  490. // var china_map = echarts.init(document.getElementById("echartsMap"),'infographic');
  491. var china_map = echarts.init(document.getElementById("echartsMap"));
  492. let newArr = [];
  493. // 地图数据
  494. this.echartsData.trad_map.forEach(item => {
  495. let name = item.province_name;
  496. name = name.split('省')[0];
  497. name = name.split('市')[0];
  498. name = name.split('特别行政区')[0];
  499. name = name.split('壮族自治区')[0];
  500. name = name.split('回族自治区')[0];
  501. name = name.split('维吾尔自治区')[0];
  502. name = name.split('自治区')[0];
  503. let obj = {
  504. name,
  505. value: item.order_num
  506. };
  507. newArr.push(obj);
  508. })
  509. console.info(newArr);
  510. var option = {
  511. tooltip: {
  512. trigger: 'item'
  513. },
  514. visualMap: {
  515. show: false,
  516. x: 'left',
  517. y: 'bottom',
  518. splitList: [
  519. {
  520. start: 0,
  521. end: 80
  522. }, {
  523. start: 80,
  524. end: 90
  525. },
  526. {
  527. start: 90,
  528. end: 100
  529. }, {
  530. start: 100
  531. },
  532. ],
  533. color: ['#ED7453', '#A2E157', '#0034E4', '#002390']
  534. },
  535. series: [{
  536. type: 'map',
  537. mapType: 'china',
  538. // roam: true,
  539. label: {
  540. normal: {
  541. show: false
  542. },
  543. emphasis: {
  544. show: false
  545. }
  546. },
  547. itemStyle: {
  548. areaColor: '#002390',
  549. borderColor: '#4478F5'
  550. },
  551. data: newArr
  552. },]
  553. };
  554. china_map.setOption(option);
  555. }
  556. }
  557. })
  558. </script>