stickiness.vue 11 KB

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