trend.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  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 class="uni-group">
  7. <view class="uni-sub-title hide-on-phone">各指标趋势分析</view>
  8. </view>
  9. </view>
  10. <view class="uni-container">
  11. <view class="uni-stat--x flex p-1015">
  12. <view class="uni-stat--app-select">
  13. <uni-data-select collection="opendb-app-list" @change="changeAppid" field="appid as value, name as text" orderby="text asc" :defItem="1" label="应用选择" v-model="query.appid" :clear="false" />
  14. <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" />
  15. </view>
  16. </view>
  17. <view class="uni-stat--x flex">
  18. <uni-stat-tabs label="日期选择" :current="currentDateTab" mode="date" @change="changeTimeRange" />
  19. <uni-datetime-picker type="datetimerange" :end="new Date().getTime()" v-model="query.start_time" returnType="timestamp" :clearIcon="false" class="uni-stat-datetime-picker" :class="{'uni-stat__actived': currentDateTab < 0 && !!query.start_time.length}" @change="useDatetimePicker" />
  20. <uni-stat-tabs label="维度选择" type="box" :current="currentDimensionTab" :tabs="dimensionTabs" @change="changeDimensionTab" />
  21. </view>
  22. <view class="uni-stat--x">
  23. <uni-stat-tabs label="平台选择" type="boldLine" mode="platform" v-model="query.platform_id" @change="changePlatform" />
  24. <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" />
  25. </view>
  26. <uni-stat-panel :items="panelData" />
  27. <view class="uni-stat--x p-m">
  28. <view class="label-text mb-l">
  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" echartsH5 echartsApp
  34. tooltipFormat="tooltipCustom" :errorMessage="errorMessage"/>
  35. </view>
  36. </view>
  37. <view class="uni-stat--x p-m">
  38. <uni-stat-table :data="tableData" :filedsMap="fieldsMap" :loading="loading" />
  39. <view class="uni-pagination-box">
  40. <uni-pagination show-icon show-page-size :page-size="options.pageSize"
  41. :current="options.pageCurrent" :total="options.total" @change="changePageCurrent"
  42. @pageSizeChange="changePageSize" />
  43. </view>
  44. </view>
  45. </view>
  46. <!-- #ifndef H5 -->
  47. <fix-window />
  48. <!-- #endif -->
  49. </view>
  50. </template>
  51. <script>
  52. import {
  53. mapfields,
  54. stringifyQuery,
  55. stringifyField,
  56. stringifyGroupField,
  57. getTimeOfSomeDayAgo,
  58. division,
  59. format,
  60. formatDate,
  61. getFieldTotal,
  62. debounce
  63. } from '@/js_sdk/uni-stat/util.js'
  64. import fieldsMap from './fieldsMap.js'
  65. export default {
  66. data() {
  67. return {
  68. tableName: 'uni-stat-result',
  69. fieldsMap,
  70. query: {
  71. dimension: "day",
  72. appid: '',
  73. platform_id: '',
  74. uni_platform: '',
  75. version_id: '',
  76. channel_id: '',
  77. start_time: [],
  78. },
  79. options: {
  80. pageSize: 20,
  81. pageCurrent: 1, // 当前页
  82. total: 0, // 数据总量
  83. },
  84. loading: false,
  85. currentDateTab: 1,
  86. currentDimensionTab: 1,
  87. tableData: [],
  88. panelData: fieldsMap.filter(f => f.hasOwnProperty('value')),
  89. chartData: {},
  90. chartTab: 'new_user_count',
  91. channelData: [],
  92. tabIndex: 0,
  93. errorMessage: "",
  94. }
  95. },
  96. computed: {
  97. chartTabs() {
  98. const tabs = []
  99. fieldsMap.forEach(item => {
  100. const {
  101. field: _id,
  102. title: name
  103. } = item
  104. const isTab = item.hasOwnProperty('value')
  105. if (_id && name && isTab) {
  106. tabs.push({
  107. _id,
  108. name
  109. })
  110. }
  111. })
  112. return tabs
  113. },
  114. dimensionTabs() {
  115. const tabs = [{
  116. _id: 'hour',
  117. name: '按时'
  118. }, {
  119. _id: 'day',
  120. name: '按日'
  121. }, {
  122. _id: 'week',
  123. name: '按周'
  124. }, {
  125. _id: 'month',
  126. name: '按月'
  127. }];
  128. if (!this.getDays()) {
  129. this.query.dimension = 'hour'
  130. tabs.forEach((tab, index) => {
  131. if (tab._id === 'hour') {
  132. tab.disabled = false
  133. } else {
  134. tab.disabled = true
  135. }
  136. })
  137. this.currentDimensionTab = 0
  138. } else {
  139. //this.query.dimension = 'day'
  140. tabs.forEach((tab, index) => {
  141. if (tab._id === 'hour') {
  142. tab.disabled = false
  143. } else {
  144. tab.disabled = false
  145. }
  146. })
  147. //this.currentDimensionTab = 1
  148. }
  149. return tabs
  150. },
  151. channelQuery() {
  152. const platform_id = this.query.platform_id
  153. return stringifyQuery({
  154. platform_id
  155. })
  156. },
  157. versionQuery() {
  158. const {
  159. appid,
  160. uni_platform
  161. } = this.query
  162. const query = stringifyQuery({
  163. appid,
  164. uni_platform,
  165. })
  166. return query
  167. }
  168. },
  169. created() {
  170. this.debounceGet = debounce(() => {
  171. this.getAllData(this.query);
  172. }, 300);
  173. this.getChannelData()
  174. },
  175. watch: {
  176. query: {
  177. deep: true,
  178. handler(val) {
  179. this.debounceGet()
  180. }
  181. }
  182. },
  183. methods: {
  184. getDays() {
  185. if (!this.query.start_time.length) return true
  186. const day = 24 * 60 * 60 * 1000
  187. const [start, end] = this.query.start_time
  188. const lessTwoDay = end - start >= day
  189. return lessTwoDay
  190. },
  191. useDatetimePicker() {
  192. this.currentDateTab = -1
  193. },
  194. changeAppid(id) {
  195. this.getChannelData(id, false)
  196. },
  197. changePlatform(id, index, name, item) {
  198. this.getChannelData(null, id)
  199. this.query.version_id = 0
  200. this.query.uni_platform = item.code
  201. },
  202. changeTimeRange(id, index) {
  203. this.currentDateTab = index
  204. const day = 24 * 60 * 60 * 1000
  205. let start, end
  206. start = getTimeOfSomeDayAgo(id)
  207. if (!id) {
  208. end = getTimeOfSomeDayAgo(0) + day - 1
  209. } else {
  210. end = getTimeOfSomeDayAgo(0) - 1
  211. }
  212. this.query.start_time = [start, end]
  213. },
  214. changeDimensionTab(dimension, index){
  215. this.currentDimensionTab = index;
  216. this.query.dimension = dimension;
  217. },
  218. changePageCurrent(e) {
  219. this.options.pageCurrent = e.current
  220. this.getTabelData(this.query)
  221. },
  222. changePageSize(pageSize) {
  223. this.options.pageSize = pageSize
  224. this.options.pageCurrent = 1 // 重置分页
  225. this.getTabelData(this.query)
  226. },
  227. changeChartTab(id, index, name) {
  228. this.tabIndex = index
  229. this.getChartData(this.query, id, name)
  230. },
  231. getAllData(query) {
  232. if (!query.appid) {
  233. this.errorMessage = "请先选择应用";
  234. return; // 如果appid为空,则不进行查询
  235. }
  236. this.errorMessage = "";
  237. this.getPanelData()
  238. this.getChartData(query)
  239. this.getTabelData(query)
  240. },
  241. getChartData(query, field = this.chartTabs[this.tabIndex]._id, name = this.chartTabs[this.tabIndex].name) {
  242. // this.chartData = {}
  243. query = stringifyQuery(query, true, ['uni_platform'])
  244. const dimension = this.query.dimension
  245. //console.log('query: ', query, this.query.dimension)
  246. const db = uniCloud.database()
  247. db.collection(this.tableName)
  248. .where(query)
  249. .field(`${stringifyField(fieldsMap, field)}, start_time`)
  250. .groupBy('start_time')
  251. .groupField(stringifyGroupField(fieldsMap, field))
  252. .orderBy('start_time', 'asc')
  253. .get({
  254. getCount: true
  255. })
  256. .then(res => {
  257. const {
  258. count,
  259. data
  260. } = res.result
  261. const options = {
  262. categories: [],
  263. series: [{
  264. name,
  265. data: []
  266. }]
  267. }
  268. let mapper = fieldsMap.filter(f => f.field === field)
  269. mapper = JSON.parse(JSON.stringify(mapper))
  270. delete mapper[0].value
  271. mapper[0].formatter = ''
  272. for (const item of data) {
  273. mapfields(mapper, item, item)
  274. const x = formatDate(item.start_time, dimension)
  275. let y = item[field]
  276. options.series[0].data.push(y)
  277. options.categories.push(x)
  278. }
  279. this.chartData = options
  280. }).catch((err) => {
  281. console.error(err)
  282. // err.message 错误信息
  283. // err.code 错误码
  284. }).finally(() => {
  285. this.loading = false
  286. })
  287. },
  288. getTabelData(query) {
  289. const {
  290. pageCurrent
  291. } = this.options
  292. query = stringifyQuery(query, true, ['uni_platform'])
  293. this.options.pageCurrent = 1 // 重置分页
  294. this.loading = true
  295. const db = uniCloud.database()
  296. db.collection(this.tableName)
  297. .where(query)
  298. .field(stringifyField(fieldsMap))
  299. .groupBy('start_time')
  300. .groupField(stringifyGroupField(fieldsMap))
  301. .orderBy('start_time', 'desc')
  302. .skip((pageCurrent - 1) * this.options.pageSize)
  303. .limit(this.options.pageSize)
  304. .get({
  305. getCount: true
  306. })
  307. .then(res => {
  308. const {
  309. count,
  310. data
  311. } = res.result
  312. for (const item of data) {
  313. let date = item.start_time
  314. if (date) {
  315. const dimension = this.query.dimension
  316. item.start_time = formatDate(date, dimension)
  317. }
  318. mapfields(fieldsMap, item, item)
  319. }
  320. this.tableData = []
  321. this.options.total = count
  322. this.tableData = data
  323. }).catch((err) => {
  324. console.error(err)
  325. // err.message 错误信息
  326. // err.code 错误码
  327. }).finally(() => {
  328. this.loading = false
  329. })
  330. },
  331. getPanelData() {
  332. let cloneQuery = JSON.parse(JSON.stringify(this.query))
  333. cloneQuery.dimension = 'day'
  334. let query = stringifyQuery(cloneQuery, false, ['uni_platform'])
  335. const db = uniCloud.database()
  336. const subTable = db.collection(this.tableName)
  337. .where(query)
  338. .field(`${stringifyField(fieldsMap)},stat_date`)
  339. .groupBy('appid')
  340. .groupField(stringifyGroupField(fieldsMap))
  341. .orderBy('stat_date', 'desc')
  342. .get()
  343. .then(res => {
  344. const item = res.result.data[0]
  345. item && (item.total_devices = 0)
  346. getFieldTotal.call(this, cloneQuery)
  347. this.panelData = []
  348. this.panelData = mapfields(fieldsMap, item)
  349. })
  350. },
  351. navTo(id) {
  352. const url = `/pages/uni-stat/overview/overview?id=${id}`
  353. uni.navigateTo({
  354. url
  355. })
  356. },
  357. //获取渠道信息
  358. getChannelData(appid, platform_id) {
  359. this.query.channel_id = ''
  360. const db = uniCloud.database()
  361. const condition = {}
  362. //对应应用
  363. appid = appid ? appid : this.query.appid
  364. if (appid) {
  365. condition.appid = appid
  366. }
  367. //对应平台
  368. platform_id = platform_id ? platform_id : this.query.platform_id
  369. if (platform_id) {
  370. condition.platform_id = platform_id
  371. }
  372. let platformTemp = db.collection('uni-stat-app-platforms')
  373. .field('_id, name')
  374. .getTemp()
  375. let channelTemp = db.collection('uni-stat-app-channels')
  376. .where(condition)
  377. .field('_id, channel_name, create_time, platform_id')
  378. .getTemp()
  379. db.collection(channelTemp, platformTemp)
  380. .orderBy('platform_id', 'asc')
  381. .get()
  382. .then(res => {
  383. let data = res.result.data
  384. let channels = []
  385. if (data.length > 0) {
  386. let channelName
  387. for (let i in data) {
  388. channelName = data[i].channel_name ? data[i].channel_name : '默认'
  389. if (data[i].platform_id.length > 0) {
  390. channelName = data[i].platform_id[0].name + '-' + channelName
  391. }
  392. channels.push({
  393. value: data[i]._id,
  394. text: channelName
  395. })
  396. }
  397. }
  398. this.channelData = channels
  399. })
  400. .catch((err) => {
  401. console.error(err)
  402. // err.message 错误信息
  403. // err.code 错误码
  404. }).finally(() => {})
  405. }
  406. }
  407. }
  408. </script>
  409. <style>
  410. </style>