overview.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. <template>
  2. <!-- 对应页面:设备统计-概况 -->
  3. <view class="fix-top-window">
  4. <view class="uni-header">
  5. <uni-stat-breadcrumb class="uni-stat-breadcrumb-on-phone" />
  6. </view>
  7. <view class="uni-container">
  8. <view class="uni-stat--x flex">
  9. <uni-data-select collection="opendb-app-list" field="appid as value, name as text" orderby="text asc"
  10. :defItem="1" label="应用选择" v-model="query.appid" :clear="false" />
  11. <uni-data-select collection="opendb-app-versions" :where="versionQuery"
  12. field="_id as value, version as text" orderby="text asc" label="版本选择" v-model="query.version_id" />
  13. <view class="flex">
  14. <uni-stat-tabs label="日期选择" :current="currentDateTab" mode="date" :today="true"
  15. @change="changeTimeRange" />
  16. <uni-datetime-picker type="daterange" :end="new Date().getTime()" v-model="query.start_time"
  17. returnType="timestamp" :clearIcon="false" class="uni-stat-datetime-picker"
  18. :class="{'uni-stat__actived': currentDateTab < 0 && !!query.start_time.length}"
  19. @change="useDatetimePicker" />
  20. </view>
  21. </view>
  22. <view class="uni-stat--x">
  23. <uni-stat-tabs label="平台选择" type="boldLine" mode="platform" v-model="query.platform_id"
  24. @change="changePlatform" />
  25. </view>
  26. <uni-stat-panel :items="panelData" :contrast="true" />
  27. <view class="uni-stat--x p-m">
  28. <view class="uni-stat-card-header">
  29. 趋势图
  30. </view>
  31. <uni-stat-tabs type="box" v-model="chartTab" :tabs="chartTabs" class="mb-l" @change="changeChartTab" />
  32. <view class="uni-charts-box">
  33. <qiun-data-charts type="area" :chartData="chartData" :eopts="eopts" echartsH5 echartsApp tooltipFormat="tooltipCustom"/>
  34. </view>
  35. </view>
  36. <view class="dispaly-grid">
  37. <view class="uni-stat--x p-m">
  38. <view class="uni-stat-card-header">
  39. <view>受访页 TOP10</view>
  40. <view class="uni-stat-card-header-link" @click="navTo('/pages/uni-stat/page-res/page-res')">查看更多
  41. </view>
  42. </view>
  43. <uni-table :loading="loading" border stripe emptyText="暂无数据">
  44. <uni-tr>
  45. <template v-for="(mapper, index) in resFieldsMap">
  46. <uni-th v-if="mapper.title" :key="index" align="center">
  47. <!-- #ifdef MP -->
  48. {{mapper.title}}
  49. <!-- #endif -->
  50. <!-- #ifndef MP -->
  51. <uni-tooltip>
  52. {{mapper.title}}
  53. <uni-icons v-if=" mapper.tooltip" type="help" color="#666" />
  54. <template v-if="mapper.tooltip" v-slot:content>
  55. <view class="uni-stat-tooltip-s">
  56. {{mapper.tooltip}}
  57. </view>
  58. </template>
  59. </uni-tooltip>
  60. <!-- #endif -->
  61. </uni-th>
  62. </template>
  63. </uni-tr>
  64. <uni-tr v-for="(item ,i) in resTableData" :key="i">
  65. <template v-for="(mapper, index) in resFieldsMap">
  66. <uni-td v-if="mapper.title" :key="index" :align="index === 0 ? 'left' : 'center'">
  67. {{item[mapper.field] !== undefined ? item[mapper.field] : '-'}}
  68. </uni-td>
  69. </template>
  70. </uni-tr>
  71. </uni-table>
  72. </view>
  73. <view class="uni-stat--x uni-stat-card p-m">
  74. <view class="uni-stat-card-header">
  75. <view>入口页 TOP10</view>
  76. <view class="uni-stat-card-header-link" @click="navTo('/pages/uni-stat/page-ent/page-ent')">查看更多
  77. </view>
  78. </view>
  79. <uni-table :loading="loading" border stripe emptyText="暂无数据">
  80. <uni-tr>
  81. <template v-for="(mapper, index) in entFieldsMap">
  82. <uni-th v-if="mapper.title" :key="index" align="center">
  83. <!-- #ifdef MP -->
  84. {{mapper.title}}
  85. <!-- #endif -->
  86. <!-- #ifndef MP -->
  87. <uni-tooltip>
  88. {{mapper.title}}
  89. <uni-icons v-if=" mapper.tooltip" type="help" color="#666" />
  90. <template v-if="mapper.tooltip" v-slot:content>
  91. <view class="uni-stat-tooltip-s">
  92. {{mapper.tooltip}}
  93. </view>
  94. </template>
  95. </uni-tooltip>
  96. <!-- #endif -->
  97. </uni-th>
  98. </template>
  99. </uni-tr>
  100. <uni-tr v-for="(item ,i) in entTableData" :key="i">
  101. <template v-for="(mapper, index) in entFieldsMap">
  102. <uni-td v-if="mapper.title" :key="index" :align="index === 0 ? 'left' : 'center'">
  103. {{item[mapper.field] !== undefined ? item[mapper.field] : '-'}}
  104. </uni-td>
  105. </template>
  106. </uni-tr>
  107. </uni-table>
  108. </view>
  109. </view>
  110. </view>
  111. <!-- #ifndef H5 -->
  112. <fix-window />
  113. <!-- #endif -->
  114. </view>
  115. </template>
  116. <script>
  117. import {
  118. mapfields,
  119. stringifyQuery,
  120. stringifyField,
  121. stringifyGroupField,
  122. getTimeOfSomeDayAgo,
  123. division,
  124. format,
  125. formatDate,
  126. parseDateTime,
  127. getFieldTotal,
  128. debounce
  129. } from '@/js_sdk/uni-stat/util.js'
  130. import {
  131. fieldsMap,
  132. resFieldsMap,
  133. entFieldsMap
  134. } from './fieldsMap.js'
  135. const panelOption = fieldsMap.filter(f => f.hasOwnProperty('value'))
  136. export default {
  137. data() {
  138. return {
  139. tableName: 'uni-stat-result',
  140. fieldsMap,
  141. resFieldsMap,
  142. entFieldsMap,
  143. query: {
  144. dimension: 'hour',
  145. appid: '',
  146. version_id: '',
  147. platform_id: '',
  148. uni_platform:'',
  149. start_time: [],
  150. },
  151. options: {
  152. pageCurrent: 1, // 当前页
  153. total: 0, // 数据总量
  154. pageSizeIndex: 0, // 与 pageSizeRange 一起计算得出 pageSize
  155. pageSizeRange: [10, 20, 50, 100],
  156. },
  157. loading: false,
  158. currentDateTab: 2,
  159. chartTab: 'new_user_count',
  160. tableData: [],
  161. resTableData: [],
  162. entTableData: [],
  163. panelData: panelOption,
  164. chartData: {},
  165. eopts: {
  166. seriesTemplate: [{
  167. itemStyle: {
  168. borderWidth: 2,
  169. borderColor: '#1890FF',
  170. color: '#1890FF'
  171. },
  172. areaStyle: {
  173. color: {
  174. colorStops: [{
  175. offset: 0,
  176. color: '#1890FF', // 0% 处的颜色
  177. }, {
  178. offset: 1,
  179. color: '#FFFFFF' // 100% 处的颜色
  180. }]
  181. }
  182. }
  183. }, {
  184. // smooth: false,
  185. lineStyle: {
  186. color: '#ea7ccc',
  187. width: 2,
  188. type: 'dashed'
  189. },
  190. itemStyle: {
  191. borderWidth: 1,
  192. borderColor: '#ea7ccc',
  193. color: '#ea7ccc'
  194. },
  195. areaStyle: null
  196. }]
  197. },
  198. tabIndex: 0,
  199. tabName: '新增设备',
  200. }
  201. },
  202. onLoad(option) {
  203. const {
  204. appid
  205. } = option
  206. if (appid) {
  207. this.query.appid = appid
  208. }
  209. },
  210. computed: {
  211. pageSize() {
  212. const {
  213. pageSizeRange,
  214. pageSizeIndex
  215. } = this.options
  216. return pageSizeRange[pageSizeIndex]
  217. },
  218. chartTabs() {
  219. const tabs = []
  220. fieldsMap.forEach(item => {
  221. const _id = item.field
  222. const name = item.title
  223. if (_id && name) {
  224. tabs.push({
  225. _id,
  226. name
  227. })
  228. }
  229. })
  230. return tabs
  231. },
  232. versionQuery() {
  233. const {
  234. appid,
  235. uni_platform
  236. } = this.query
  237. const query = stringifyQuery({
  238. appid,
  239. uni_platform,
  240. })
  241. return query
  242. }
  243. },
  244. created() {
  245. this.debounceGet = debounce(() => this.getAllData(this.query))
  246. },
  247. watch: {
  248. query: {
  249. deep: true,
  250. handler(val) {
  251. this.options.pageCurrent = 1 // 重置分页
  252. this.debounceGet()
  253. }
  254. }
  255. },
  256. methods: {
  257. useDatetimePicker() {
  258. this.currentDateTab = null
  259. },
  260. changePlatform(id, index, name, item) {
  261. this.query.version_id = 0
  262. console.log('item.code',item.code);
  263. this.query.uni_platform = item.code
  264. },
  265. changeTimeRange(id, index) {
  266. this.currentDateTab = index
  267. const day = 24 * 60 * 60 * 1000
  268. let start, end
  269. start = getTimeOfSomeDayAgo(id)
  270. if (!id) {
  271. end = getTimeOfSomeDayAgo(0) + day - 1
  272. } else {
  273. end = getTimeOfSomeDayAgo(0) - 1
  274. }
  275. this.query.start_time = [start, end]
  276. },
  277. changePageCurrent(e) {
  278. this.options.pageCurrent = e.current
  279. this.getChartData(this.query)
  280. },
  281. changePageSize(e) {
  282. const {
  283. value
  284. } = e.detail
  285. this.options.pageCurrent = 1 // 重置分页
  286. this.options.pageSizeIndex = value
  287. this.getChartData(this.query)
  288. },
  289. changeChartTab(id, index, name) {
  290. this.tabIndex = index
  291. this.tabName = name
  292. this.getChartData(this.query, id, name)
  293. },
  294. getAllData(query) {
  295. this.getPanelData()
  296. this.getChartData(query)
  297. this.getPageData(query, 'res')
  298. this.getPageData(query, 'ent')
  299. },
  300. getDays() {
  301. if (!this.query.start_time.length) return true
  302. const day = 24 * 60 * 60 * 1000
  303. const [start, end] = this.query.start_time
  304. const lessTwoDay = end - start >= day
  305. return lessTwoDay
  306. },
  307. getPanelData() {
  308. const {
  309. appid,
  310. platform_id,
  311. version_id
  312. } = this.query
  313. let query = stringifyQuery({
  314. appid,
  315. platform_id,
  316. version_id,
  317. start_time: [getTimeOfSomeDayAgo(1), new Date().getTime()]
  318. })
  319. const db = uniCloud.database()
  320. const subTable = db.collection(this.tableName)
  321. .where(query)
  322. .field(
  323. `${stringifyField(fieldsMap)},dimension,stat_date`
  324. )
  325. .groupBy(`stat_date, dimension`)
  326. .groupField(stringifyGroupField(fieldsMap))
  327. .orderBy('stat_date', 'desc')
  328. .get()
  329. .then(res => {
  330. const data = res.result.data
  331. const today = data.find(item => item.stat_date === parseDateTime(getTimeOfSomeDayAgo(0), '',
  332. '')) || {}
  333. today.total_devices = 0
  334. const yesterday = data.find(item => item.dimension === 'day' && item.stat_date ===
  335. parseDateTime(getTimeOfSomeDayAgo(1), '', ''))
  336. this.panelData = []
  337. this.panelData = mapfields(fieldsMap, today)
  338. this.panelData.map(item => {
  339. mapfields(fieldsMap, yesterday, item, '', 'contrast')
  340. })
  341. getFieldTotal.call(this, query)
  342. })
  343. },
  344. getChartData(query, field = this.chartTabs[this.tabIndex]._id, name = this.chartTabs[this.tabIndex].name) {
  345. // this.chartData = {}
  346. const {
  347. pageCurrent
  348. } = this.options
  349. const days = this.currentDateTab
  350. const date = getTimeOfSomeDayAgo(days)
  351. const day = 24 * 60 * 60 * 1000
  352. let start_time
  353. // 范围小于一天的日期(今日、昨日)做增大一天处理,目的是对比当前日期和前一天(今日和昨日、昨日和上前日)
  354. if (!this.getDays()) {
  355. const start = date - day
  356. const end = date + day - 1
  357. query = JSON.parse(JSON.stringify(query))
  358. start_time = query.start_time = [start, end]
  359. // query.dimension = 'hour'
  360. }
  361. query = stringifyQuery(query, true, ['uni_platform'])
  362. const db = uniCloud.database()
  363. db.collection(this.tableName)
  364. .where(query)
  365. .field(`${stringifyField(fieldsMap, field)}, start_time`)
  366. .groupBy(`start_time`)
  367. .groupField(stringifyGroupField(fieldsMap, field))
  368. .orderBy('start_time', 'asc')
  369. .get({
  370. getCount: true
  371. })
  372. .then(res => {
  373. const {
  374. count,
  375. data
  376. } = res.result
  377. const options = {
  378. categories: [],
  379. series: [{
  380. name,
  381. data: []
  382. }]
  383. }
  384. let mapper = fieldsMap.filter(f => f.field === field)
  385. mapper = JSON.parse(JSON.stringify(mapper))
  386. delete mapper[0].value
  387. mapper[0].formatter = ''
  388. if (!this.getDays()) {
  389. const [start, end] = start_time
  390. const line = options.series[1] = {
  391. name: formatDate(start),
  392. data: [],
  393. }
  394. const cont = options.series[0] = {
  395. name: formatDate(end),
  396. data: [],
  397. }
  398. for (let i = 0; i < 24; ++i) {
  399. const hour = i < 10 ? '0' + i : i
  400. const x = `${hour}:00 ~ ${hour}:59`
  401. options.categories.push(x)
  402. line.data[i] = 0
  403. cont.data[i] = 0
  404. data.forEach(item => {
  405. mapfields(mapper, item, item)
  406. let val = Number(item[field])
  407. const d = new Date(item.start_time)
  408. if (item.start_time < date) {
  409. if (d.getHours() === i) {
  410. line.data[i] = val
  411. }
  412. } else {
  413. if (d.getHours() === i) {
  414. cont.data[i] = val
  415. }
  416. }
  417. })
  418. }
  419. } else {
  420. for (const item of data) {
  421. mapfields(mapper, item, item)
  422. const x = formatDate(item.start_time, 'day')
  423. let y = Number(item[field])
  424. options.series[0].data.push(y)
  425. options.categories.push(x)
  426. }
  427. }
  428. this.chartData = options
  429. }).catch((err) => {
  430. console.error(err)
  431. // err.message 错误信息
  432. // err.code 错误码
  433. }).finally(() => {})
  434. },
  435. getAppAccessTimes(query) {
  436. const db = uniCloud.database()
  437. return db.collection(this.tableName)
  438. .where(query)
  439. .groupBy('appid')
  440. .groupField(`sum(page_visit_count) as total_app_access`)
  441. .get()
  442. },
  443. getPageData(query, type) {
  444. query = JSON.parse(JSON.stringify(query))
  445. query.dimension = 'day'
  446. query = stringifyQuery(query,false,['uni_platform'])
  447. console.log('page data ',query);
  448. const {
  449. pageCurrent
  450. } = this.options
  451. const mapping = this[`${type}FieldsMap`]
  452. const field = mapping[1].field
  453. this.loading = true
  454. const db = uniCloud.database()
  455. const filterAppid = stringifyQuery({
  456. appid: this.query.appid
  457. })
  458. const mainTableTemp = db.collection('uni-stat-pages')
  459. .where(filterAppid)
  460. .field('_id, title, path')
  461. .getTemp()
  462. const subTableTemp = db.collection('uni-stat-page-result')
  463. .where(`${query} && ${field} > 0`)
  464. .getTemp()
  465. db.collection(subTableTemp, mainTableTemp)
  466. .field(
  467. `${stringifyField(mapping, field)}, stat_date, page_id`
  468. )
  469. .groupBy("page_id")
  470. .groupField(stringifyGroupField(mapping, field))
  471. .orderBy(field, 'desc')
  472. .limit(10)
  473. .get({
  474. getCount: true
  475. })
  476. .then(res => {
  477. const {
  478. count,
  479. data
  480. } = res.result
  481. let total_app_access
  482. this.getAppAccessTimes(query).then(res => {
  483. const data = res.result.data[0]
  484. total_app_access = data && data.total_app_access
  485. }).finally(() => {
  486. this[`${type}TableData`] = []
  487. for (const item of data) {
  488. item.total_app_access = total_app_access
  489. const lines = item.page_id
  490. if (Array.isArray(lines)) {
  491. delete(item.page_id)
  492. const line = lines[0]
  493. if (line && Object.keys(line).length) {
  494. for (const key in line) {
  495. if (key !== '_id') {
  496. item[key] = line[key]
  497. }
  498. }
  499. }
  500. }
  501. mapfields(mapping, item, item)
  502. this[`${type}TableData`].push(item)
  503. }
  504. this.loading = false
  505. })
  506. }).catch((err) => {
  507. console.error(err)
  508. // err.message 错误信息
  509. // err.code 错误码
  510. }).finally(() => {})
  511. },
  512. navTo(url) {
  513. if (!url) return
  514. uni.navigateTo({
  515. url
  516. })
  517. }
  518. }
  519. }
  520. </script>
  521. <style>
  522. .uni-stat-card-header {
  523. display: flex;
  524. justify-content: space-between;
  525. color: #555;
  526. font-size: 14px;
  527. font-weight: 600;
  528. padding: 10px 0;
  529. margin-bottom: 15px;
  530. }
  531. .uni-stat-card-header-link {
  532. cursor: pointer;
  533. }
  534. </style>