stickiness.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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-title">用户忠诚度</view> -->
  8. <view class="uni-sub-title hide-on-phone">用户忠诚度用户对您应用的访问深度及访问频次情况。助您了解用户对应用的粘度,尤其在对内容改进后,效果是否有所提升</view>
  9. </view>
  10. </view>
  11. <view class="uni-container">
  12. <view class="uni-stat--x flex">
  13. <uni-data-select collection="opendb-app-list" field="appid as value, name as text" orderby="text asc"
  14. :defItem="1" label="应用选择" @change="changeAppid" v-model="query.appid" :clear="false" />
  15. <uni-data-select collection="opendb-app-versions" :where="versionQuery"
  16. field="_id as value, version as text" orderby="text asc" label="版本选择" v-model="query.version_id" />
  17. <view class="flex">
  18. <uni-stat-tabs label="日期选择" :current="currentDateTab" mode="date" @change="changeTimeRange" />
  19. <uni-datetime-picker type="daterange" :end="new Date().getTime()" v-model="query.start_time"
  20. returnType="timestamp" :clearIcon="false" class="uni-stat-datetime-picker"
  21. :class="{'uni-stat__actived': currentDateTab < 0 && !!query.start_time.length}"
  22. @change="useDatetimePicker" />
  23. </view>
  24. </view>
  25. <view class="uni-stat--x">
  26. <uni-stat-tabs label="平台选择" type="boldLine" mode="platform" v-model="query.platform_id"
  27. @change="changePlatform" />
  28. <uni-data-select v-if="query.platform_id && query.platform_id.indexOf('==') === -1"
  29. :localdata="channelData" label="渠道选择" v-model="query.channel_id"></uni-data-select>
  30. </view>
  31. <view class="uni-stat--x mb-l" style="padding-top: 0;">
  32. <view class="mb-m line-bottom">
  33. <uni-stat-tabs type="boldLine" :tabs="types" v-model="type"
  34. style="line-height: 40px; margin-bottom: -17px;" />
  35. </view>
  36. <view class="p-m">
  37. <view class="uni-charts-box">
  38. <qiun-data-charts type="pie" :chartData="chartData" echartsH5 echartsApp />
  39. </view>
  40. </view>
  41. </view>
  42. <view class="uni-stat--x p-m">
  43. <uni-stat-table :data="tableData" :filedsMap="fieldsMap" :loading="loading" />
  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. getTimeOfSomeDayAgo,
  56. division,
  57. format,
  58. debounce
  59. } from '@/js_sdk/uni-stat/util.js'
  60. import fieldsMap from './fieldsMap.js'
  61. export default {
  62. data() {
  63. return {
  64. fieldsMap,
  65. query: {
  66. // dimension: "hour",
  67. appid: '',
  68. platform_id: '',
  69. uni_platform: '',
  70. version_id: '',
  71. channel_id: '',
  72. start_time: [],
  73. },
  74. loading: false,
  75. currentDateTab: 1,
  76. tableData: [],
  77. panelData: fieldsMap.filter(f => f.hasOwnProperty('value')),
  78. chartData: {},
  79. type: 'visit_depth_data',
  80. types: [{
  81. _id: 'visit_depth_data',
  82. name: '访问页数'
  83. }, {
  84. _id: 'duration_data',
  85. name: '访问时长'
  86. }],
  87. field: 'visit_devices',
  88. fields: [{
  89. _id: 'visit_devices',
  90. name: '访问人数'
  91. }, {
  92. _id: 'visit_times',
  93. name: '访问次数'
  94. }],
  95. options: {
  96. visit_depth_data: {
  97. prefix: 'p',
  98. title: '页',
  99. value: [1, 2, 3, 4, 5, 10]
  100. },
  101. duration_data: {
  102. prefix: 's',
  103. title: '秒',
  104. value: [0, 3, 6, 11, 21, 31, 51, 100]
  105. }
  106. },
  107. channelData: []
  108. }
  109. },
  110. computed: {
  111. fieldName() {
  112. return this.fields.forEach(item => {
  113. if (item._id === this.field) {
  114. return item.name
  115. }
  116. })
  117. },
  118. channelQuery() {
  119. const platform_id = this.query.platform_id
  120. return stringifyQuery({
  121. platform_id
  122. })
  123. },
  124. versionQuery() {
  125. const {
  126. appid,
  127. uni_platform
  128. } = this.query
  129. const query = stringifyQuery({
  130. appid,
  131. uni_platform,
  132. })
  133. return query
  134. }
  135. },
  136. created() {
  137. this.debounceGet = debounce(() => this.getAllData(this.query))
  138. this.getChannelData()
  139. },
  140. watch: {
  141. query: {
  142. deep: true,
  143. handler(val) {
  144. this.debounceGet()
  145. }
  146. },
  147. type() {
  148. this.getAllData(this.query)
  149. },
  150. field() {
  151. this.getAllData(this.query)
  152. }
  153. },
  154. methods: {
  155. useDatetimePicker() {
  156. this.currentDateTab = -1
  157. },
  158. changeAppid(id) {
  159. this.getChannelData(id, false)
  160. },
  161. changePlatform(id, index, name, item) {
  162. this.getChannelData(null, id)
  163. this.query.version_id = 0
  164. this.query.uni_platform = item.code
  165. },
  166. changeTimeRange(id, index) {
  167. this.currentDateTab = index
  168. const start = getTimeOfSomeDayAgo(id),
  169. end = getTimeOfSomeDayAgo(0) - 1
  170. this.query.start_time = [start, end]
  171. },
  172. // 此处 util 中的 groupField 不满足需求,特殊处理 groupField
  173. createStr(fields, type = "visit_depth_data") {
  174. const l = fields.length
  175. const p = this.options[type].prefix
  176. const value = this.options[type].value
  177. const strArr = value.map(item => {
  178. return fields.map(field => {
  179. return `sum(${type}.${field}.${p + '_' + item}) as ${l > 1 ? field + '_' + p +'_'+item : p + '_' + item}`
  180. })
  181. })
  182. const str = strArr.join()
  183. return str
  184. },
  185. parseChars(str) {
  186. str = str.split('_')
  187. const option = this.options[this.type]
  188. let chars = option.title
  189. const strArr = option.value.forEach((val, i) => {
  190. const next = option.value[i + 1]
  191. if (val === Number(str[str.length - 1])) {
  192. if (!next) {
  193. chars = val + '+' + chars
  194. } else if (val + 1 === next) {
  195. chars = val + chars
  196. } else {
  197. chars = val + '-' + (next - 1) + chars
  198. }
  199. }
  200. })
  201. return chars
  202. },
  203. getAllData(query) {
  204. this.getChartData(query, this.field, this.fieldName)
  205. this.getTabelData(query)
  206. },
  207. getChartData(query, field = this.field, name = this.fields.find(f => f._id === this.field).name) {
  208. // this.chartData = {}
  209. query = stringifyQuery(query, null, ['uni_platform'])
  210. const groupField = this.createStr([field], this.type)
  211. const db = uniCloud.database()
  212. db.collection('uni-stat-loyalty-result')
  213. .where(query)
  214. .groupBy('appid')
  215. .groupField(groupField)
  216. .orderBy('start_time', 'asc')
  217. .get({
  218. getCount: true
  219. })
  220. .then(res => {
  221. let {
  222. count,
  223. data
  224. } = res.result
  225. data = data[0]
  226. const options = {
  227. series: [{
  228. data: [],
  229. }]
  230. }
  231. for (const key in data) {
  232. if (key !== 'appid') {
  233. const x = this.parseChars(key)
  234. const y = data[key]
  235. options.series[0].data.push({
  236. name: x,
  237. value: y
  238. })
  239. }
  240. }
  241. this.chartData = options
  242. }).catch((err) => {
  243. console.error(err)
  244. // err.message 错误信息
  245. // err.code 错误码
  246. }).finally(() => {
  247. this.loading = false
  248. })
  249. },
  250. getTabelData(query) {
  251. query = stringifyQuery(query, null, ['uni_platform'])
  252. const groupField = this.createStr(['visit_devices', 'visit_times'], this.type)
  253. this.fieldsMap[0].title = this.types.find(t => t._id === this.type).name
  254. this.loading = true
  255. const db = uniCloud.database()
  256. db.collection('uni-stat-loyalty-result')
  257. .where(query)
  258. .groupBy('appid')
  259. .groupField(groupField)
  260. .orderBy('start_time', 'asc')
  261. .get({
  262. getCount: true
  263. })
  264. .then(res => {
  265. const {
  266. count,
  267. data
  268. } = res.result
  269. const type = this.type
  270. const rows = []
  271. let splitor = this.options[type].prefix
  272. splitor = `_${splitor}_`
  273. for (const item of data) {
  274. for (const key in item) {
  275. if (key !== 'appid') {
  276. const row = {}
  277. const keys = key.split(splitor)
  278. row.name = keys[1]
  279. row[keys[0]] = item[key]
  280. rows.push(row)
  281. }
  282. }
  283. }
  284. const tableData = []
  285. // 归并得出访问人数 users、访问次数 times 的总和,用于计算占比
  286. const total = {}
  287. const reducer = (previousValue, currentValue) => previousValue + currentValue;
  288. let users = rows.filter(row => row.visit_devices)
  289. .map(row => row.visit_devices)
  290. users = users.length ? users.reduce(reducer) : 0
  291. let times = rows.filter(row => row.visit_times)
  292. .map(row => row.visit_times)
  293. times = times.length ? times.reduce(reducer) : 0
  294. total.visit_times = times
  295. total.visit_devices = users
  296. this.options[type].value.forEach(val => {
  297. const item = {}
  298. item.name = val + 'p'
  299. rows.forEach(row => {
  300. if (Number(row.name) === val) {
  301. for (const key in row) {
  302. if (key !== name) {
  303. item[key] = row[key]
  304. item['total_' + key] = total[key]
  305. }
  306. }
  307. }
  308. })
  309. item.name = this.parseChars(String(val))
  310. tableData.push(item)
  311. })
  312. for (const item of tableData) {
  313. mapfields(fieldsMap, item, item)
  314. }
  315. // this.options.total = count
  316. this.tableData = []
  317. this.tableData = tableData
  318. }).catch((err) => {
  319. console.error(err)
  320. // err.message 错误信息
  321. // err.code 错误码
  322. }).finally(() => {
  323. this.loading = false
  324. })
  325. },
  326. //获取渠道信息
  327. getChannelData(appid, platform_id) {
  328. this.query.channel_id = ''
  329. const db = uniCloud.database()
  330. const condition = {}
  331. //对应应用
  332. appid = appid ? appid : this.query.appid
  333. if (appid) {
  334. condition.appid = appid
  335. }
  336. //对应平台
  337. platform_id = platform_id ? platform_id : this.query.platform_id
  338. if (platform_id) {
  339. condition.platform_id = platform_id
  340. }
  341. let platformTemp = db.collection('uni-stat-app-platforms')
  342. .field('_id, name')
  343. .getTemp()
  344. let channelTemp = db.collection('uni-stat-app-channels')
  345. .where(condition)
  346. .field('_id, channel_name, create_time, platform_id')
  347. .getTemp()
  348. db.collection(channelTemp, platformTemp)
  349. .orderBy('platform_id', 'asc')
  350. .get()
  351. .then(res => {
  352. let data = res.result.data
  353. let channels = []
  354. if (data.length > 0) {
  355. let channelName
  356. for (let i in data) {
  357. channelName = data[i].channel_name ? data[i].channel_name : '默认'
  358. if (data[i].platform_id.length > 0) {
  359. channelName = data[i].platform_id[0].name + '-' + channelName
  360. }
  361. channels.push({
  362. value: data[i]._id,
  363. text: channelName
  364. })
  365. }
  366. }
  367. this.channelData = channels
  368. })
  369. .catch((err) => {
  370. console.error(err)
  371. // err.message 错误信息
  372. // err.code 错误码
  373. }).finally(() => {})
  374. }
  375. }
  376. }
  377. </script>
  378. <style lang="scss">
  379. .flex {
  380. display: flex;
  381. flex-wrap: wrap;
  382. align-items: center;
  383. }
  384. .label-text {
  385. font-size: 14px;
  386. color: #666;
  387. margin: auto 0;
  388. margin-right: 5px;
  389. }
  390. .line-bottom {
  391. border-bottom: 2px solid #eee;
  392. }
  393. .uni-stat-panel {
  394. box-shadow: unset;
  395. border-bottom: 1px solid #eee;
  396. padding: 0;
  397. margin: 0 15px;
  398. }
  399. </style>