overview.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  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 p-1015">
  9. <view class="uni-stat--app-select">
  10. <uni-data-select collection="opendb-app-list" field="appid as value, name as text" orderby="text asc" :defItem="1" label="应用选择" v-model="query.appid" :clear="false" />
  11. <uni-data-select collection="opendb-app-versions" :where="versionQuery" class="ml-m" field="_id as value, version as text, uni_platform as label, create_date as date" format="{label} - {text}" orderby="date desc" label="版本选择" v-model="query.version_id" />
  12. </view>
  13. </view>
  14. <view class="uni-stat--x flex">
  15. <uni-stat-tabs label="日期选择" :current="currentDateTab" mode="date" :today="true"
  16. @change="changeTimeRange" />
  17. <uni-datetime-picker type="datetimerange" :end="new Date().getTime()" v-model="query.start_time"
  18. returnType="timestamp" :clearIcon="false" class="uni-stat-datetime-picker"
  19. :class="{'uni-stat__actived': currentDateTab < 0 && !!query.start_time.length}"
  20. @change="useDatetimePicker" />
  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. <uni-data-select ref="version-select" v-if="query.platform_id && query.platform_id.indexOf('==') === -1" collection="uni-stat-app-channels" :where="channelQuery" class="p-channel" field="_id as value, channel_name as text" orderby="text asc" label="渠道/场景值选择" v-model="query.channel_id" />
  26. </view>
  27. <uni-stat-panel :items="panelData" :contrast="true" />
  28. <view class="uni-stat--x p-m">
  29. <view class="uni-stat-card-header">
  30. 趋势图
  31. </view>
  32. <uni-stat-tabs type="box" v-model="chartTab" :tabs="chartTabs" class="mb-l" @change="changeChartTab" />
  33. <view class="uni-charts-box">
  34. <qiun-data-charts type="area" :chartData="chartData" :eopts="eopts" echartsH5 echartsApp
  35. tooltipFormat="tooltipCustom" :errorMessage="errorMessage"/>
  36. </view>
  37. </view>
  38. </view>
  39. <!-- #ifndef H5 -->
  40. <fix-window />
  41. <!-- #endif -->
  42. </view>
  43. </template>
  44. <script>
  45. import {
  46. mapfields,
  47. stringifyQuery,
  48. stringifyField,
  49. stringifyGroupField,
  50. getTimeOfSomeDayAgo,
  51. division,
  52. format,
  53. formatDate,
  54. parseDateTime,
  55. getFieldTotal,
  56. debounce
  57. } from '@/js_sdk/uni-stat/util.js'
  58. import {
  59. fieldsMap,
  60. resFieldsMap,
  61. entFieldsMap
  62. } from './fieldsMap.js'
  63. const panelOption = fieldsMap.filter(f => f.hasOwnProperty('value'))
  64. export default {
  65. data() {
  66. return {
  67. tableName: 'uni-stat-result',
  68. fieldsMap,
  69. resFieldsMap,
  70. entFieldsMap,
  71. query: {
  72. dimension: 'hour',
  73. appid: '',
  74. platform_id: '',
  75. uni_platform: '',
  76. version_id: '',
  77. start_time: [],
  78. },
  79. options: {
  80. pageCurrent: 1, // 当前页
  81. total: 0, // 数据总量
  82. pageSizeIndex: 0, // 与 pageSizeRange 一起计算得出 pageSize
  83. pageSizeRange: [10, 20, 50, 100],
  84. },
  85. loading: false,
  86. currentDateTab: 2,
  87. chartTab: 'new_user_count',
  88. tableData: [],
  89. resTableData: [],
  90. entTableData: [],
  91. panelData: panelOption,
  92. chartData: {},
  93. eopts: {
  94. seriesTemplate: [{
  95. itemStyle: {
  96. borderWidth: 2,
  97. borderColor: '#1890FF',
  98. color: '#1890FF'
  99. },
  100. areaStyle: {
  101. color: {
  102. colorStops: [{
  103. offset: 0,
  104. color: '#1890FF', // 0% 处的颜色
  105. }, {
  106. offset: 1,
  107. color: '#FFFFFF' // 100% 处的颜色
  108. }]
  109. }
  110. }
  111. }, {
  112. // smooth: false,
  113. lineStyle: {
  114. color: '#ea7ccc',
  115. width: 2,
  116. type: 'dashed'
  117. },
  118. itemStyle: {
  119. borderWidth: 1,
  120. borderColor: '#ea7ccc',
  121. color: '#ea7ccc'
  122. },
  123. areaStyle: null
  124. }]
  125. },
  126. tabIndex: 0,
  127. errorMessage: "",
  128. }
  129. },
  130. onLoad(option) {
  131. const {
  132. appid
  133. } = option
  134. if (appid) {
  135. this.query.appid = appid
  136. }
  137. },
  138. computed: {
  139. pageSize() {
  140. const {
  141. pageSizeRange,
  142. pageSizeIndex
  143. } = this.options
  144. return pageSizeRange[pageSizeIndex]
  145. },
  146. chartTabs() {
  147. const tabs = []
  148. fieldsMap.forEach(item => {
  149. const _id = item.field
  150. const name = item.title
  151. if (_id && name) {
  152. tabs.push({
  153. _id,
  154. name
  155. })
  156. }
  157. })
  158. return tabs
  159. },
  160. versionQuery() {
  161. const {
  162. appid,
  163. uni_platform
  164. } = this.query
  165. const query = stringifyQuery({
  166. appid,
  167. uni_platform
  168. })
  169. return query
  170. },
  171. channelQuery() {
  172. const {
  173. appid,
  174. platform_id,
  175. } = this.query
  176. const query = stringifyQuery({
  177. appid,
  178. platform_id
  179. })
  180. return query
  181. },
  182. },
  183. created() {
  184. this.debounceGet = debounce(() => {
  185. this.getAllData(this.query);
  186. }, 300);
  187. },
  188. watch: {
  189. query: {
  190. deep: true,
  191. handler(val) {
  192. this.options.pageCurrent = 1 // 重置分页
  193. this.debounceGet()
  194. }
  195. }
  196. },
  197. methods: {
  198. useDatetimePicker() {
  199. this.currentDateTab = null
  200. },
  201. changePlatform(id, index, name, item) {
  202. this.query.version_id = 0
  203. this.query.uni_platform = item.code
  204. },
  205. changeTimeRange(id, index) {
  206. this.currentDateTab = index
  207. const day = 24 * 60 * 60 * 1000
  208. let start, end
  209. start = getTimeOfSomeDayAgo(id)
  210. if (!id) {
  211. end = getTimeOfSomeDayAgo(0) + day - 1
  212. } else {
  213. end = getTimeOfSomeDayAgo(0) - 1
  214. }
  215. this.query.start_time = [start, end]
  216. },
  217. changePageCurrent(e) {
  218. this.options.pageCurrent = e.current
  219. this.getChartData(this.query)
  220. },
  221. changePageSize(e) {
  222. const {
  223. value
  224. } = e.detail
  225. this.options.pageCurrent = 1 // 重置分页
  226. this.options.pageSizeIndex = value
  227. this.getChartData(this.query)
  228. },
  229. changeChartTab(id, index, name) {
  230. this.tabIndex = index
  231. this.getChartData(this.query, id, name)
  232. },
  233. getAllData(query) {
  234. if (!query.appid) {
  235. this.errorMessage = "请先选择应用";
  236. return; // 如果appid为空,则不进行查询
  237. }
  238. this.errorMessage = "";
  239. this.getPanelData()
  240. this.getChartData(query)
  241. },
  242. getDays() {
  243. if (!this.query.start_time.length) return true
  244. const day = 24 * 60 * 60 * 1000
  245. const [start, end] = this.query.start_time
  246. const lessTwoDay = end - start >= day
  247. return lessTwoDay
  248. },
  249. getPanelData() {
  250. const {
  251. appid,
  252. platform_id,
  253. version_id
  254. } = this.query
  255. const query = stringifyQuery({
  256. appid,
  257. platform_id,
  258. version_id,
  259. start_time: [getTimeOfSomeDayAgo(1), new Date().getTime()]
  260. })
  261. const db = uniCloud.database()
  262. const subTable = db.collection(this.tableName)
  263. .where(query)
  264. .field(
  265. `${stringifyField(fieldsMap)},dimension,stat_date`
  266. )
  267. .groupBy(`stat_date, dimension`)
  268. .groupField(stringifyGroupField(fieldsMap))
  269. .orderBy('stat_date', 'desc')
  270. .get()
  271. .then(res => {
  272. const data = res.result.data
  273. const today = data.find(item => item.stat_date === parseDateTime(getTimeOfSomeDayAgo(0), '',
  274. '')) || {}
  275. today.total_users = 0
  276. const yesterday = data.find(item => item.dimension === 'day' && item.stat_date ===
  277. parseDateTime(getTimeOfSomeDayAgo(1), '', ''))
  278. this.panelData = []
  279. this.panelData = mapfields(fieldsMap, today)
  280. this.panelData.map(item => {
  281. mapfields(fieldsMap, yesterday, item, '', 'contrast')
  282. })
  283. getFieldTotal.call(this, query, 'total_users')
  284. })
  285. },
  286. getChartData(query, field = this.chartTabs[this.tabIndex]._id, name = this.chartTabs[this.tabIndex].name) {
  287. // this.chartData = {}
  288. const {
  289. pageCurrent
  290. } = this.options
  291. const days = this.currentDateTab
  292. const date = getTimeOfSomeDayAgo(days)
  293. const day = 24 * 60 * 60 * 1000
  294. let start_time
  295. // 范围小于一天的日期(今日、昨日)做增大一天处理,目的是对比当前日期和前一天(今日和昨日、昨日和上前日)
  296. if (!this.getDays()) {
  297. const start = date - day
  298. const end = date + day - 1
  299. query = JSON.parse(JSON.stringify(query))
  300. start_time = query.start_time = [start, end]
  301. }
  302. query = stringifyQuery(query, true, ['uni_platform'])
  303. const db = uniCloud.database()
  304. db.collection(this.tableName)
  305. .where(query)
  306. .field(`${stringifyField(fieldsMap, field)}, start_time`)
  307. .groupBy(`start_time`)
  308. .groupField(stringifyGroupField(fieldsMap, field))
  309. .orderBy('start_time', 'asc')
  310. .get({
  311. getCount: true
  312. })
  313. .then(res => {
  314. const {
  315. count,
  316. data
  317. } = res.result
  318. const options = {
  319. categories: [],
  320. series: [{
  321. name,
  322. data: []
  323. }]
  324. }
  325. let mapper = fieldsMap.filter(f => f.field === field)
  326. mapper = JSON.parse(JSON.stringify(mapper))
  327. delete mapper[0].value
  328. mapper[0].formatter = ''
  329. if (!this.getDays()) {
  330. const [start, end] = start_time
  331. const line = options.series[1] = {
  332. name: formatDate(start),
  333. data: [],
  334. }
  335. const cont = options.series[0] = {
  336. name: formatDate(end),
  337. data: [],
  338. }
  339. for (let i = 0; i < 24; ++i) {
  340. const hour = i < 10 ? '0' + i : i
  341. const x = `${hour}:00 ~ ${hour}:59`
  342. options.categories.push(x)
  343. line.data[i] = 0
  344. cont.data[i] = 0
  345. data.forEach(item => {
  346. mapfields(mapper, item, item)
  347. let val = Number(item[field])
  348. const d = new Date(item.start_time)
  349. if (item.start_time < date) {
  350. if (d.getHours() === i) {
  351. line.data[i] = val
  352. }
  353. } else {
  354. if (d.getHours() === i) {
  355. cont.data[i] = val
  356. }
  357. }
  358. })
  359. }
  360. } else {
  361. for (const item of data) {
  362. mapfields(mapper, item, item)
  363. const x = formatDate(item.start_time, 'day')
  364. let y = Number(item[field])
  365. options.series[0].data.push(y)
  366. options.categories.push(x)
  367. }
  368. }
  369. this.chartData = options
  370. }).catch((err) => {
  371. console.error(err)
  372. // err.message 错误信息
  373. // err.code 错误码
  374. }).finally(() => {})
  375. },
  376. getAppAccessTimes(query) {
  377. const db = uniCloud.database()
  378. return db.collection(this.tableName)
  379. .where(query)
  380. .groupBy('appid')
  381. .groupField(`sum(page_visit_count) as total_app_access`)
  382. .get()
  383. },
  384. navTo(url) {
  385. if (!url) return
  386. uni.navigateTo({
  387. url
  388. })
  389. }
  390. }
  391. }
  392. </script>
  393. <style>
  394. .uni-stat-card-header {
  395. display: flex;
  396. justify-content: space-between;
  397. color: #555;
  398. font-size: 14px;
  399. font-weight: 600;
  400. padding: 10px 0;
  401. margin-bottom: 15px;
  402. }
  403. .uni-stat-card-header-link {
  404. cursor: pointer;
  405. }
  406. </style>