funnelChart.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. <template>
  2. <view class="uni-stat--x p-m">
  3. <view class="uni-stat-card-header">漏斗分析</view>
  4. <!-- 时间纬度 -->
  5. <view class="flex">
  6. <uni-stat-tabs type="box" :current="dateTabs.index" :tabs="dateTabs.list" @change="dateTabsChange" />
  7. <uni-datetime-picker type="date" v-model="dateTabs.time" :end="Date.now()" return-type="timestamp" :clear-icon="false" class="uni-stat-datetime-picker" @change="datePickerChange" />
  8. <view class="uni-stat--tips" v-if="dateTabs.timeStr">当前时间范围:{{ dateTabs.timeStr }}</view>
  9. </view>
  10. <!-- 漏斗 -->
  11. <view class="uni-charts-box" v-if="!notData">
  12. <qiun-data-charts type="funnel" :chartData="chartData" :opts="opts" :errorMessage="errorMessage" />
  13. </view>
  14. <view class="uni-charts-box flex center" v-else>
  15. <view >暂无数据</view>
  16. </view>
  17. </view>
  18. </template>
  19. <script>
  20. import {
  21. mapfields,
  22. stringifyQuery,
  23. stringifyField,
  24. stringifyGroupField,
  25. getTimeOfSomeDayAgo,
  26. division,
  27. format,
  28. formatDate,
  29. parseDateTime,
  30. getFieldTotal,
  31. debounce
  32. } from '@/js_sdk/uni-stat/util.js'
  33. import timeUtil from "@/js_sdk/uni-stat/timeUtil.js"
  34. import {
  35. fieldsMap,
  36. } from '../fieldsMap.js'
  37. export default {
  38. props: {
  39. // 组件外部查询条件,一般包含 appid version_id platform_id
  40. query: {
  41. type: [Object],
  42. default: function() {
  43. return {}
  44. }
  45. }
  46. },
  47. data() {
  48. return {
  49. tableName: 'uni-stat-pay-result',
  50. fieldsMap,
  51. chartData: {},
  52. errorMessage:"",
  53. notData: false,
  54. opts: {
  55. color: ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4", "#ea7ccc"],
  56. padding: [15, 15, 0, 15],
  57. extra: {
  58. funnel: {
  59. activeOpacity: 0.3,
  60. activeWidth: 10,
  61. border: true,
  62. borderWidth: 2,
  63. borderColor: "#FFFFFF",
  64. fillOpacity: 1,
  65. labelAlign: "right",
  66. linearType: "custom",
  67. minSize: 20
  68. }
  69. }
  70. },
  71. // 时间选项
  72. dateTabs: {
  73. time: Date.now(),
  74. timeStr:"",
  75. index: 0,
  76. list: [
  77. { _id: "day", name: '日维度' },
  78. { _id: "week", name: '周维度' },
  79. { _id: "month", name: '月维度' }
  80. ]
  81. }
  82. }
  83. },
  84. created() {
  85. this.getCloudDataDebounce = debounce(() => {
  86. this.getCloudData();
  87. }, 400);
  88. this.getCloudDataDebounce();
  89. },
  90. methods: {
  91. calcPercentage(v1, v2) {
  92. return v2 > 0 ? parseFloat((v1 / v2 * 100).toFixed(2)) : 0;
  93. },
  94. // 获取云端数据
  95. getCloudData() {
  96. let query = this.query;
  97. if (!query.appid){
  98. this.errorMessage = "请先选择应用";
  99. return;
  100. }
  101. this.errorMessage = "";
  102. let insideQuery = this.getWhere();
  103. let where = {
  104. ...query,
  105. ...insideQuery
  106. };
  107. const day = 24 * 60 * 60 * 1000;
  108. let start_time;
  109. where = stringifyQuery(where, false, ['uni_platform']);
  110. //console.log('where: ', where);
  111. const db = uniCloud.database();
  112. const subTable = db.collection(this.tableName)
  113. .where(where)
  114. .field(`${stringifyField(fieldsMap)}, dimension, stat_date.date_str as stat_time, start_time`)
  115. .groupBy(`null`)
  116. .groupField(stringifyGroupField(fieldsMap))
  117. .get()
  118. .then(res => {
  119. let data = res.result.data;
  120. if (!data.length) {
  121. this.errorMessage = "暂无数据";
  122. return;
  123. }
  124. this.errorMessage = "";
  125. //console.log('data: ', data);
  126. data.map((item) => {
  127. for (let key in item) {
  128. if (key.indexOf("_amount") > 1) {
  129. item[key] = Number((item[key] / 100).toFixed(2));
  130. }
  131. }
  132. });
  133. let {
  134. activity_device_count = 0,
  135. activity_user_count = 0,
  136. pay_user_count = 0,
  137. } = data[0] || {};
  138. this.notData = !activity_device_count && !activity_user_count && !pay_user_count ? true : false;
  139. let chartData = {
  140. series: [{
  141. data: [
  142. {
  143. "name": "活跃设备数量",
  144. "value": activity_device_count,
  145. "centerText": `${activity_device_count}`, "labelText": `活跃设备数`
  146. },
  147. {
  148. "name": "活跃用户数量",
  149. "value": activity_user_count,
  150. "centerText": `${activity_user_count}`,
  151. "labelText": `活跃用户数(用户转化率:${this.calcPercentage(activity_user_count,activity_device_count)}%)`
  152. },
  153. {
  154. "name": "支付用户数量",
  155. "value": pay_user_count,
  156. "centerText": `${pay_user_count}`,
  157. "labelText": `支付用户数(支付转化率:${this.calcPercentage(pay_user_count,activity_user_count)}%)`,
  158. },
  159. ],
  160. }]
  161. };
  162. this.chartData = chartData;
  163. })
  164. },
  165. // 监听 - 日期标签更改
  166. dateTabsChange(id, index) {
  167. this.dateTabs.index = index;
  168. this.getCloudData();
  169. },
  170. // 监听 - 日期选择更改
  171. datePickerChange(time) {
  172. this.dateTabs.time = time;
  173. this.getCloudData();
  174. },
  175. // 获取查询条件
  176. getWhere() {
  177. let time = this.dateTabs.time; // 当前选择的时间
  178. let dimension = this.dateTabs.list[this.dateTabs.index]._id || "day"; // 获取时间纬度
  179. let start_time = [];
  180. if (dimension === "day") {
  181. let { startTime, endTime } = timeUtil.getOffsetStartAndEnd("day", 0, time);
  182. start_time = [startTime, endTime];
  183. } else if (dimension === "week") {
  184. let { startTime, endTime } = timeUtil.getOffsetStartAndEnd("week",0, time);
  185. start_time = [startTime, endTime];
  186. } else if (dimension === "month") {
  187. let { startTime, endTime } = timeUtil.getOffsetStartAndEnd("month", 0, time);
  188. start_time = [startTime, endTime];
  189. }
  190. this.dateTabs.timeStr = `${timeUtil.timeFormat(start_time[0])} ~ ${timeUtil.timeFormat(start_time[1])}`;
  191. return {
  192. dimension, // 时间纬度
  193. start_time, // 时间范围
  194. }
  195. }
  196. },
  197. watch: {
  198. query: {
  199. deep: true,
  200. handler(val) {
  201. this.getCloudDataDebounce();
  202. }
  203. }
  204. },
  205. computed: {
  206. }
  207. }
  208. </script>
  209. <style lang="scss" scoped>
  210. .flex.center{
  211. justify-content: center;
  212. align-items: center;
  213. color: #666;
  214. }
  215. </style>