visitor.wpy 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. <style lang="less">
  2. page {
  3. background-color: #eceff4;
  4. }
  5. .page-container {
  6. width: 100%;
  7. height: 100%;
  8. box-sizing: border-box;
  9. box-sizing: border-box;
  10. padding: 30rpx 44rpx;
  11. font-family: PingFang SC;
  12. }
  13. .visitor-title {
  14. display: flex;
  15. justify-content: space-between;
  16. align-items: flex-end;
  17. .visitor-title-left {
  18. font-size: 48rpx;
  19. font-weight: 400;
  20. line-height: 68rpx;
  21. color: rgba(0, 0, 0, 1);
  22. }
  23. .visitor-title-right {
  24. font-family: Montserrat;
  25. display: flex;
  26. font-size: 26rpx;
  27. font-weight: 400;
  28. line-height: 36rpx;
  29. color: rgba(77, 82, 98, 1);
  30. }
  31. }
  32. .visitor-info {
  33. margin-top: 60rpx;
  34. display: flex;
  35. justify-content: space-between;
  36. height: 212rpx;
  37. width: 662rpx;
  38. border-radius: 16rpx;
  39. background: rgba(255, 255, 255, 1);
  40. padding: 60rpx 40rpx;
  41. box-sizing: border-box;
  42. .visitor-info-item {
  43. .info-title {
  44. font-size: 26rpx;
  45. font-weight: 400;
  46. line-height: 36rpx;
  47. color: rgba(196, 196, 196, 1);
  48. }
  49. .info-num {
  50. display: flex;
  51. padding-top: 14rpx;
  52. .info-num-value {
  53. font-family: Montserrat;
  54. font-size: 46rpx;
  55. font-weight: 400;
  56. line-height: 42rpx;
  57. color: rgba(0, 0, 0, 1);
  58. margin-right: 6rpx;
  59. }
  60. .info-num-unit {
  61. font-size: 18rpx;
  62. font-weight: 400;
  63. line-height: 26rpx;
  64. color: rgba(196, 196, 196, 1);
  65. }
  66. }
  67. }
  68. }
  69. .visitor-num {
  70. margin-top: 26rpx;
  71. height: 350rpx;
  72. width: 100%;
  73. .meeting-time-legend {
  74. display: flex;
  75. .legend-item {
  76. display: flex;
  77. align-items: center;
  78. margin-right: 40rpx;
  79. .legend-item-shape {
  80. height: 12rpx;
  81. width: 30rpx;
  82. border-radius: 6rpx;
  83. background: #5e8bcf;
  84. margin-right: 12rpx;
  85. &.grey {
  86. background: #9fb7cd;
  87. }
  88. }
  89. .legend-item-text {
  90. font-size: 20rpx;
  91. font-weight: 400;
  92. line-height: 28rpx;
  93. color: #4d5262;
  94. }
  95. }
  96. }
  97. }
  98. .visitor-function {
  99. padding: 36rpx 40rpx;
  100. margin-top: 30rpx;
  101. // height: 276px;
  102. border-radius: 16rpx;
  103. background: rgba(255, 255, 255, 1);
  104. .visitor-card-item {
  105. display: flex;
  106. align-items: center;
  107. .visitor-card-item-pic {
  108. width: 32rpx;
  109. height: 32rpx;
  110. .img {
  111. width: 100%;
  112. height: 100%;
  113. }
  114. }
  115. .visitor-card-item-pic:nth-child(1) {
  116. padding-bottom: 4rpx;
  117. }
  118. .visitor-card-item-name {
  119. margin-left: 24rpx;
  120. margin-right: 38rpx;
  121. font-size: 24rpx;
  122. font-weight: 400;
  123. line-height: 34rpx;
  124. width: 104rpx;
  125. }
  126. .visitor-card-item-line {
  127. height: 10rpx;
  128. width: 220rpx;
  129. background: rgba(166, 206, 227, 1);
  130. border-radius: 0px 6rpx 6rpx 0px;
  131. &.nav {
  132. background: #1f78b4;
  133. }
  134. }
  135. }
  136. .visitor-card-item:first-child {
  137. margin-top: 66rpx;
  138. }
  139. .visitor-card-item:last-child {
  140. margin-top: 22rpx;
  141. margin-bottom: 30rpx;
  142. }
  143. }
  144. .visitor-card-ttile {
  145. margin-bottom: 34rpx;
  146. font-size: 34rpx;
  147. font-weight: 600;
  148. line-height: 48rpx;
  149. color: rgba(0, 0, 0, 1);
  150. }
  151. .notNum {
  152. height: 800rpx;
  153. display: flex;
  154. justify-content: center;
  155. align-items: center;
  156. .notNum-text {
  157. margin-top: 20rpx;
  158. font-family: PingFang SC;
  159. font-size: 34rpx;
  160. font-weight: 400;
  161. line-height: 48rpx;
  162. color: #c4c4c4;
  163. }
  164. }
  165. .logo-text {
  166. margin-top:260rpx;
  167. display: flex;
  168. justify-content: center;
  169. width: 100%;
  170. height: 24rpx;
  171. padding: 98rpx 0 80rpx;
  172. }
  173. </style>
  174. <template>
  175. <div class="page-container">
  176. <div class="visitor-title">
  177. <div class="visitor-title-left">
  178. 访客统计
  179. </div>
  180. <div
  181. class="visitor-title-right"
  182. @click="changeMonth"
  183. >
  184. <span>{{yearMonthText}}</span>
  185. <div>
  186. <image
  187. style="height:36rpx;width:36rpx"
  188. src="{{h5StaticPath}}/page-portrait/statistics-icon-down.svg"
  189. >
  190. </image>
  191. </div>
  192. </div>
  193. </div>
  194. <div v-if="isPageShow">
  195. <div class="visitor-info">
  196. <div
  197. class="visitor-info-item"
  198. v-for="(item,idx) in infoArr"
  199. :key="idx"
  200. >
  201. <div class="info-title">{{item.title}}</div>
  202. <div class="info-num">
  203. <div class="info-num-value">{{item.value}}</div>
  204. <div class="info-num-unit">{{item.unit}}</div>
  205. </div>
  206. </div>
  207. </div>
  208. <div class="visitor-num">
  209. <div class="meeting-time-legend">
  210. <div class="legend-item">
  211. <div class="legend-item-shape"></div>
  212. <div class="legend-item-text">访问人数</div>
  213. </div>
  214. <div class="legend-item">
  215. <div class="legend-item-shape grey"></div>
  216. <div class="legend-item-text">接待时长</div>
  217. </div>
  218. </div>
  219. <image
  220. v-if="!imgSrc==''"
  221. src="{{imgSrc}}"
  222. ></image>
  223. <ff-canvas
  224. v-if="imgSrc==''"
  225. canvas-id="visitor-num-chart"
  226. :onInit="numChartData"
  227. ></ff-canvas>
  228. </div>
  229. <div class="visitor-function">
  230. <div class="visitor-card-ttile">访客使用的功能</div>
  231. <div
  232. class="visitor-card-item"
  233. v-for="(item,idx) in functionArr"
  234. :key="idx"
  235. >
  236. <div class="visitor-card-item-pic">
  237. <image
  238. class="img"
  239. src="{{h5StaticPath +'/page-portrait/' + item.img}}"
  240. />
  241. </div>
  242. <div class="visitor-card-item-name">{{item.title}}</div>
  243. <div
  244. class="visitor-card-item-line {{item.actClass}}"
  245. style="width:{{item.wth}}rpx"
  246. ></div>
  247. </div>
  248. </div>
  249. <image
  250. class="logo-text"
  251. src="{{h5StaticPath}}/page-home/tenatslink.svg"
  252. ></image>
  253. </div>
  254. <div
  255. class="notNum"
  256. v-if="!isPageShow"
  257. >
  258. <div style="text-align: center;">
  259. <image
  260. style="height:200rpx;width:200rpx"
  261. src="{{h5StaticPath}}/page-portrait/nodata.png"
  262. />
  263. <div class="notNum-text">
  264. 暂无数据
  265. </div>
  266. </div>
  267. </div>
  268. <van-popup
  269. custom-style="height: 50%"
  270. overlay-style="background: rgba(0, 0, 0, 0.4)"
  271. position="bottom"
  272. round
  273. @close="visible = false"
  274. :show="visible"
  275. >
  276. <div>
  277. <monthcom
  278. @checkTime="checkTime"
  279. :yearMonthText="yearMonthText"
  280. ></monthcom>
  281. </div>
  282. </van-popup>
  283. </div>
  284. </template>
  285. <script>
  286. import wepy from '@wepy/core'
  287. import store from '@/store'
  288. import config from '@/config'
  289. import moment from 'moment'
  290. import { mapState } from '@wepy/x'
  291. import { checkLogin, checkHasUserInfo } from '@/service/user'
  292. import { queryInvitationStatisticsHttp, queeyBuriedEventLogHttp } from '@/packagesEnv/api/portrait'
  293. let numChart = null
  294. let resultData = {
  295. data: [
  296. ]
  297. }
  298. function numChartData(F2, config) {
  299. let data = resultData.data
  300. numChart = new F2.Chart({ ...config, appendPadding: [30, 30, 15, 5] })
  301. numChart.clear()
  302. numChart.source(data, {
  303. 'x': {
  304. type: 'linear',
  305. tickInterval: 2,
  306. min: 1,
  307. max: 31
  308. }
  309. })
  310. numChart.guide().text({
  311. top: false,
  312. limitInPlot: true,
  313. position: ['max', 'min'], // 文本的起始位置,值为原始数据值,支持 callback
  314. content: '日期', // 显示的文本内容
  315. style: {
  316. fill: '#C4C4C4', // 文本颜色
  317. fontSize: '10' // 文本大小
  318. }, // 文本的图形样式属性
  319. offsetX: 20, // x 方向的偏移量
  320. offsetY: 12 // y 方向偏移量
  321. })
  322. numChart.guide().text({
  323. position: ['min', 'max'], // 文本的起始位置,值为原始数据值,支持 callback
  324. content: '时长', // 显示的文本内容
  325. style: {
  326. fill: '#C4C4C4', // 文本颜色
  327. fontSize: '10' // 文本大小
  328. }, // 文本的图形样式属性
  329. offsetX: -4, // x 方向的偏移量
  330. offsetY: -20 // y 方向偏移量
  331. })
  332. numChart.tooltip(false)
  333. numChart.point().position('x*y')
  334. .size('r', [0, 30])
  335. .shape('circle')
  336. .style({
  337. opacity: 0.6
  338. })
  339. numChart.render()
  340. return numChart
  341. }
  342. wepy.page({
  343. store,
  344. data: {
  345. h5StaticPath: config.h5StaticPath,
  346. yearMonth: moment().add(-1, 'months').format('YYYYMM'),
  347. yearMonthText: moment().add(-1, 'months').format('YYYY.MM'),
  348. infoArr: [
  349. { id: '111', title: '累计邀约', value: '--', unit: '人' },
  350. { id: '222', title: '接待时长', value: '--', unit: '小时' },
  351. { id: '333', title: '平均', value: '--', unit: '小时/天' }
  352. ],
  353. functionArr: [
  354. { id: 'wifi', title: '连接WiFi', img: 'icon-wifi.svg', value: '--', wth: 0, actClass: '' },
  355. { id: 'nav', title: '导航', img: 'icon-nav.svg', value: '--', wth: 0, actClass: 'nav' }
  356. ],
  357. numChartData,
  358. visible: false, // 弹窗
  359. isPageShow: false,
  360. imgSrc: '' // canvas 图片
  361. },
  362. computed: {
  363. ...mapState({ userInfo: state => state.user.userInfo, token: state => state.user.token })
  364. },
  365. onLoad() {
  366. checkHasUserInfo()
  367. this.getData()
  368. },
  369. onReady() { },
  370. methods: {
  371. canvasToImg() { // 将canvas转为图片
  372. wx.canvasToTempFilePath({
  373. width: 331,
  374. height: 140,
  375. destWidth: 100,
  376. destHeight: 100,
  377. canvas: numChartData,
  378. canvasId: 'visitor-num-chart',
  379. fileType: 'png',
  380. success: (res) => {
  381. this.imgSrc = res.tempFilePath
  382. },
  383. fail: res => {
  384. console.log('转图片失败', res)
  385. }
  386. }, this)
  387. },
  388. async getData() {
  389. let res = await this.getInvitationStatistics()
  390. if (res && res.visitorTotal) { // 有访客,显示界面,再请求
  391. await this.getBuriedEventLog()
  392. this.isPageShow = true
  393. }
  394. },
  395. getInvitationStatistics() {
  396. let endDate = ''
  397. if (moment().format('YYYYMM') === this.yearMonth) {
  398. endDate = moment().add(1, 'days').format('YYYYMMDD')
  399. } else {
  400. endDate = moment(this.yearMonth).add(1, 'months').startOf('month').format('YYYYMMDD')
  401. }
  402. let paramObj = {
  403. startDate: this.yearMonth + '01',
  404. endDate: endDate
  405. }
  406. return queryInvitationStatisticsHttp(paramObj).then(res => {
  407. this.isPageShow = false
  408. if (JSON.stringify(res.data) === '{}') { return };
  409. let resData = JSON.parse(JSON.stringify(res.data))
  410. this.infoArr[0].value = resData.visitorTotal
  411. this.infoArr[1].value = resData.invitationTimeTotal
  412. this.infoArr[2].value = Number.isInteger(resData.avgTimePerDay) ? resData.avgTimePerDay : resData.avgTimePerDay.toFixed(2)
  413. let charArr = []
  414. let resCharArr = resData.detailList
  415. let startMonthday = this.yearMonth + '01'
  416. let endMonthday = moment(this.yearMonth).endOf('month').format('YYYYMMDD')
  417. // 补齐坐标轴
  418. for (let i = Number(startMonthday); i <= Number(endMonthday); i++) {
  419. let flag = true
  420. let jItem = {}
  421. resCharArr.forEach(j => {
  422. let JMs = j.date // 时间戳
  423. if (i == JMs) {
  424. flag = false
  425. jItem = j
  426. }
  427. })
  428. if (flag) {
  429. charArr.push({
  430. date: i,
  431. invitationTime: 0,
  432. visitorNum: 0
  433. })
  434. } else {
  435. charArr.push(jItem)
  436. }
  437. }
  438. let visitorNumArr = [] // 人数
  439. let invitationTimeArr = [] // 时长
  440. charArr.forEach(item => {
  441. let dateItem = String(item.date)
  442. item.date = moment(dateItem).format('DD')
  443. visitorNumArr.push({ x: item.date, y: item.invitationTime, r: item.visitorNum, color: '#5E8BCF' })
  444. invitationTimeArr.push({ x: item.date, y: item.invitationTime, r: item.invitationTime, color: '#D0DBED' })
  445. })
  446. resultData.data = invitationTimeArr.concat(visitorNumArr)
  447. // return new Promise((res, reject) => {
  448. // this.canvasToImg();
  449. // })
  450. return res.data
  451. })
  452. },
  453. getBuriedEventLog() { // 访客使用的功能
  454. let funArr = ['connect_wifi', 'navigation']
  455. let $lt = ''
  456. if (moment().format('YYYYMM') === this.yearMonth) {
  457. $lt = moment().add(1, 'days').format('YYYYMMDD')
  458. } else {
  459. $lt = moment(this.yearMonth).add(1, 'months').startOf('month').format('YYYYMMDD')
  460. }
  461. funArr.forEach((item, idx) => {
  462. let paramObj = {
  463. status: true,
  464. name: funArr[idx], // connect_wifi navigation 导航
  465. createTime: {
  466. $gte: this.yearMonth + '0100000', // 开始
  467. $lt: $lt + '00000' // 结束
  468. }
  469. }
  470. return queeyBuriedEventLogHttp(paramObj).then(res => {
  471. if (res.count) {
  472. if (res.content[0].name === 'connect_wifi') {
  473. this.functionArr[0].value = res.count
  474. } else {
  475. this.functionArr[1].value = res.count
  476. }
  477. let maxValue = Math.max.apply(Math, this.functionArr.map(item => { return item.value }))
  478. this.functionArr.forEach(item => {
  479. item.wth = item.value / maxValue * 220 // 220 为最大宽度
  480. })
  481. } else {
  482. this.functionArr.forEach(item => {
  483. item.value = 0
  484. item.wth = 0
  485. })
  486. }
  487. })
  488. })
  489. },
  490. checkTime(val) {
  491. wx.uma.trackEvent('portrait_visitor_card', { key: '访客-月份切换' })
  492. this.visible = !this.visible
  493. this.chartFlag = false
  494. this.yearMonth = val
  495. this.yearMonthText = moment(this.yearMonth).format('YYYY.MM')
  496. this.getData()
  497. },
  498. changeMonth() {
  499. this.visible = !this.visible
  500. }
  501. }
  502. })
  503. </script>
  504. <config>
  505. {
  506. 'navigationBarBackgroundColor': '#eceff4',
  507. 'navigationBarTextStyle':'black',
  508. 'navigationBarTitleText': '',
  509. 'backgroundColor': '#eceff4',
  510. "usingComponents": {
  511. "ff-canvas": "../../vendor/antv/wx-f2",
  512. "van-popup": "module:@vant/weapp/dist/popup",
  513. "monthcom": "./components/monthcom"
  514. }
  515. }
  516. </config>