Преглед на файлове

!69 Vue3重构:公众号统计
Merge pull request !69 from kinlon92/dev

芋道源码 преди 2 години
родител
ревизия
c4e5419d65
променени са 2 файла, в които са добавени 482 реда и са изтрити 1 реда
  1. 79 0
      src/utils/dateUtils.ts
  2. 403 1
      src/views/mp/statistics/index.vue

+ 79 - 0
src/utils/dateUtils.ts

@@ -0,0 +1,79 @@
+/**
+ * 将毫秒,转换成时间字符串。例如说,xx 分钟
+ *
+ * @param ms 毫秒
+ * @returns {string} 字符串
+ */
+export function getDate(ms) {
+  const day = Math.floor(ms / (24 * 60 * 60 * 1000))
+  const hour = Math.floor(ms / (60 * 60 * 1000) - day * 24)
+  const minute = Math.floor(ms / (60 * 1000) - day * 24 * 60 - hour * 60)
+  const second = Math.floor(ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60)
+  if (day > 0) {
+    return day + '天' + hour + '小时' + minute + '分钟'
+  }
+  if (hour > 0) {
+    return hour + '小时' + minute + '分钟'
+  }
+  if (minute > 0) {
+    return minute + '分钟'
+  }
+  if (second > 0) {
+    return second + '秒'
+  } else {
+    return 0 + '秒'
+  }
+}
+
+export function beginOfDay(date) {
+  return new Date(date.getFullYear(), date.getMonth(), date.getDate())
+}
+
+export function endOfDay(date) {
+  return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999)
+}
+
+export function betweenDay(date1, date2) {
+  date1 = convertDate(date1)
+  date2 = convertDate(date2)
+  // 计算差值
+  return Math.floor((date2.getTime() - date1.getTime()) / (24 * 3600 * 1000))
+}
+
+export function formatDate(date, fmt) {
+  date = convertDate(date)
+  const o = {
+    'M+': date.getMonth() + 1, //月份
+    'd+': date.getDate(), //日
+    'H+': date.getHours(), //小时
+    'm+': date.getMinutes(), //分
+    's+': date.getSeconds(), //秒
+    'q+': Math.floor((date.getMonth() + 3) / 3), //季度
+    S: date.getMilliseconds() //毫秒
+  }
+  if (/(y+)/.test(fmt)) {
+    // 年份
+    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
+  }
+  for (const k in o) {
+    if (new RegExp('(' + k + ')').test(fmt)) {
+      fmt = fmt.replace(
+        RegExp.$1,
+        RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)
+      )
+    }
+  }
+  return fmt
+}
+
+export function addTime(date, time) {
+  date = convertDate(date)
+  return new Date(date.getTime() + time)
+}
+
+export function convertDate(date) {
+  if (typeof date === 'string') {
+    return new Date(date)
+  }
+  return date
+}

+ 403 - 1
src/views/mp/statistics/index.vue

@@ -1,3 +1,405 @@
 <template>
-  <span>开发中</span>
+  <!-- 搜索工作栏 -->
+  <content-wrap>
+    <el-form ref="queryForm" class="-mb-15px" :inline="true" label-width="68px">
+      <el-form-item label="公众号" prop="accountId">
+        <el-select v-model="accountId" @change="getSummary">
+          <el-option
+            v-for="item in accounts"
+            :key="parseInt(item.id)"
+            :label="item.name"
+            :value="parseInt(item.id)"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="时间范围" prop="dateRange">
+        <el-date-picker
+          v-model="dateRange"
+          style="width: 260px"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :default-time="defaultTime"
+          @change="getSummary"
+        />
+      </el-form-item>
+    </el-form>
+  </content-wrap>
+
+  <!-- 图表 -->
+  <content-wrap>
+    <el-row>
+      <el-col :span="12" class="card-box">
+        <el-card>
+          <template #header>
+            <div>
+              <span>用户增减数据</span>
+            </div>
+          </template>
+          <div class="el-table el-table--enable-row-hover el-table--medium">
+            <div ref="userSummaryChartRef" style="height: 420px"></div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="12" class="card-box">
+        <el-card>
+          <template #header>
+            <div>
+              <span>累计用户数据</span>
+            </div>
+          </template>
+          <div class="el-table el-table--enable-row-hover el-table--medium">
+            <div ref="userCumulateChartRef" style="height: 420px"></div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="12" class="card-box">
+        <el-card>
+          <template #header>
+            <div>
+              <span>消息概况数据</span>
+            </div>
+          </template>
+          <div class="el-table el-table--enable-row-hover el-table--medium">
+            <div ref="upstreamMessageChartRef" style="height: 420px"></div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="12" class="card-box">
+        <el-card>
+          <template #header>
+            <div>
+              <span>接口分析数据</span>
+            </div>
+          </template>
+          <div class="el-table el-table--enable-row-hover el-table--medium">
+            <div ref="interfaceSummaryChartRef" style="height: 420px"></div>
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+  </content-wrap>
 </template>
+
+<script setup lang="ts" name="MpStatistics">
+// 引入基本模板
+import * as echarts from 'echarts'
+import {
+  getInterfaceSummary,
+  getUpstreamMessage,
+  getUserCumulate,
+  getUserSummary
+} from '@/api/mp/statistics'
+import { addTime, beginOfDay, betweenDay, endOfDay, formatDate } from '@/utils/dateUtils'
+import { getSimpleAccountList } from '@/api/mp/account'
+
+const message = useMessage() // 消息弹窗
+
+const defaultTime = ref<[Date, Date]>([
+  new Date(2000, 1, 1, 0, 0, 0),
+  new Date(2000, 2, 1, 23, 59, 59)
+])
+const dateRange = ref([
+  beginOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7)),
+  endOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24))
+]) // -1 天
+const accountId = ref()
+const accounts = ref([
+  {
+    id: '0',
+    name: ''
+  }
+])
+
+const userSummaryChartRef = ref()
+const userCumulateChartRef = ref()
+const upstreamMessageChartRef = ref()
+const interfaceSummaryChartRef = ref()
+
+const xAxisDate = ref([] as any[]) // X 轴的日期范围
+const userSummaryOption = reactive({
+  // 用户增减数据
+  color: ['#67C23A', '#e5323e'],
+  legend: {
+    data: ['新增用户', '取消关注的用户']
+  },
+  tooltip: {},
+  xAxis: {
+    data: [] as any[] // X 轴的日期范围
+  },
+  yAxis: {
+    minInterval: 1
+  },
+  series: [
+    {
+      name: '新增用户',
+      type: 'bar',
+      label: {
+        show: true
+      },
+      barGap: 0,
+      data: [] as any[] // 新增用户的数据
+    },
+    {
+      name: '取消关注的用户',
+      type: 'bar',
+      label: {
+        show: true
+      },
+      data: [] as any[] // 取消关注的用户的数据
+    }
+  ]
+})
+const userCumulateOption = reactive({
+  // 累计用户数据
+  legend: {
+    data: ['累计用户量']
+  },
+  xAxis: {
+    type: 'category',
+    data: [] as any[]
+  },
+  yAxis: {
+    minInterval: 1
+  },
+  series: [
+    {
+      name: '累计用户量',
+      data: [] as any[], // 累计用户量的数据
+      type: 'line',
+      smooth: true,
+      label: {
+        show: true
+      }
+    }
+  ]
+})
+const upstreamMessageOption = reactive({
+  // 消息发送概况数据
+  color: ['#67C23A', '#e5323e'],
+  legend: {
+    data: ['用户发送人数', '用户发送条数']
+  },
+  tooltip: {},
+  xAxis: {
+    data: [] as any[] // X 轴的日期范围
+  },
+  yAxis: {
+    minInterval: 1
+  },
+  series: [
+    {
+      name: '用户发送人数',
+      type: 'line',
+      smooth: true,
+      label: {
+        show: true
+      },
+      data: [] as any[] // 用户发送人数的数据
+    },
+    {
+      name: '用户发送条数',
+      type: 'line',
+      smooth: true,
+      label: {
+        show: true
+      },
+      data: [] as any[] // 用户发送条数的数据
+    }
+  ]
+})
+const interfaceSummaryOption = reactive({
+  // 接口分析况数据
+  color: ['#67C23A', '#e5323e', '#E6A23C', '#409EFF'],
+  legend: {
+    data: ['被动回复用户消息的次数', '失败次数', '最大耗时', '总耗时']
+  },
+  tooltip: {},
+  xAxis: {
+    data: [] as any[] // X 轴的日期范围
+  },
+  yAxis: {},
+  series: [
+    {
+      name: '被动回复用户消息的次数',
+      type: 'bar',
+      label: {
+        show: true
+      },
+      barGap: 0,
+      data: [] as any[] // 被动回复用户消息的次数的数据
+    },
+    {
+      name: '失败次数',
+      type: 'bar',
+      label: {
+        show: true
+      },
+      data: [] as any[] // 失败次数的数据
+    },
+    {
+      name: '最大耗时',
+      type: 'bar',
+      label: {
+        show: true
+      },
+      data: [] as any[] // 最大耗时的数据
+    },
+    {
+      name: '总耗时',
+      type: 'bar',
+      label: {
+        show: true
+      },
+      data: [] as any[] // 总耗时的数据
+    }
+  ]
+})
+
+onMounted(async () => {
+  // 获取公众号下拉列表
+  await getAccountList()
+  // 加载数据
+  getSummary()
+})
+const getAccountList = async () => {
+  const data = await getSimpleAccountList()
+  accounts.value = data
+  // 默认选中第一个
+  if (accounts.value.length > 0) {
+    accountId.value = accounts.value[0].id
+  }
+}
+const getSummary = () => {
+  // 如果没有选中公众号账号,则进行提示。
+  if (!accountId) {
+    message.error('未选中公众号,无法统计数据')
+    return false
+  }
+  // 必须选择 7 天内,因为公众号有时间跨度限制为 7
+  if (betweenDay(dateRange.value[0], dateRange.value[1]) >= 7) {
+    message.error('时间间隔 7 天以内,请重新选择')
+    return false
+  }
+  xAxisDate.value = []
+  const days = betweenDay(dateRange.value[0], dateRange.value[1]) // 相差天数
+  for (let i = 0; i <= days; i++) {
+    xAxisDate.value.push(
+      formatDate(addTime(dateRange.value[0], 3600 * 1000 * 24 * i), 'yyyy-MM-dd')
+    )
+  }
+
+  // 初始化图表
+  initUserSummaryChart()
+  initUserCumulateChart()
+  initUpstreamMessageChart()
+  interfaceSummaryChart()
+}
+const initUserSummaryChart = async () => {
+  userSummaryOption.xAxis.data = []
+  userSummaryOption.series[0].data = []
+  userSummaryOption.series[1].data = []
+  try {
+    const data = await getUserSummary({
+      accountId: accountId.value,
+      date: [
+        formatDate(dateRange.value[0], 'yyyy-MM-dd HH:mm:ss'),
+        formatDate(dateRange.value[1], 'yyyy-MM-dd HH:mm:ss')
+      ]
+    })
+
+    userSummaryOption.xAxis.data = xAxisDate.value
+    // 处理数据
+    xAxisDate.value.forEach((date, index) => {
+      data.forEach((item) => {
+        // 匹配日期
+        const refDate = formatDate(new Date(item.refDate), 'yyyy-MM-dd')
+        if (refDate.indexOf(date) === -1) {
+          return
+        }
+        // 设置数据到对应的位置
+        userSummaryOption.series[0].data[index] = item.newUser
+        userSummaryOption.series[1].data[index] = item.cancelUser
+      })
+    })
+    // 绘制图表
+    const userSummaryChart = echarts.init(userSummaryChartRef.value)
+    userSummaryChart.setOption(userSummaryOption)
+  } catch {}
+}
+const initUserCumulateChart = async () => {
+  userCumulateOption.xAxis.data = []
+  userCumulateOption.series[0].data = []
+  // 发起请求
+  try {
+    const data = await getUserCumulate({
+      accountId: accountId.value,
+      date: [
+        formatDate(dateRange.value[0], 'yyyy-MM-dd HH:mm:ss'),
+        formatDate(dateRange.value[1], 'yyyy-MM-dd HH:mm:ss')
+      ]
+    })
+    userCumulateOption.xAxis.data = xAxisDate.value
+    // 处理数据
+    data.forEach((item, index) => {
+      userCumulateOption.series[0].data[index] = item.cumulateUser
+    })
+    // 绘制图表
+    const userCumulateChart = echarts.init(userCumulateChartRef.value)
+    userCumulateChart.setOption(userCumulateOption)
+  } catch {}
+}
+const initUpstreamMessageChart = async () => {
+  upstreamMessageOption.xAxis.data = []
+  upstreamMessageOption.series[0].data = []
+  upstreamMessageOption.series[1].data = []
+  // 发起请求
+  try {
+    const data = await getUpstreamMessage({
+      accountId: accountId.value,
+      date: [
+        formatDate(dateRange.value[0], 'yyyy-MM-dd HH:mm:ss'),
+        formatDate(dateRange.value[1], 'yyyy-MM-dd HH:mm:ss')
+      ]
+    })
+    upstreamMessageOption.xAxis.data = xAxisDate.value
+    // 处理数据
+    data.forEach((item, index) => {
+      upstreamMessageOption.series[0].data[index] = item.messageUser
+      upstreamMessageOption.series[1].data[index] = item.messageCount
+    })
+    // 绘制图表
+    const upstreamMessageChart = echarts.init(upstreamMessageChartRef.value)
+    upstreamMessageChart.setOption(upstreamMessageOption)
+  } catch {}
+}
+const interfaceSummaryChart = async () => {
+  interfaceSummaryOption.xAxis.data = []
+  interfaceSummaryOption.series[0].data = []
+  interfaceSummaryOption.series[1].data = []
+  interfaceSummaryOption.series[2].data = []
+  interfaceSummaryOption.series[3].data = []
+  // 发起请求
+  try {
+    const data = await getInterfaceSummary({
+      accountId: accountId.value,
+      date: [
+        formatDate(dateRange.value[0], 'yyyy-MM-dd HH:mm:ss'),
+        formatDate(dateRange.value[1], 'yyyy-MM-dd HH:mm:ss')
+      ]
+    })
+    interfaceSummaryOption.xAxis.data = xAxisDate.value
+    // 处理数据
+    data.forEach((item, index) => {
+      interfaceSummaryOption.series[0].data[index] = item.callbackCount
+      interfaceSummaryOption.series[1].data[index] = item.failCount
+      interfaceSummaryOption.series[2].data[index] = item.maxTimeCost
+      interfaceSummaryOption.series[3].data[index] = item.totalTimeCost
+    })
+    // 绘制图表
+    const interfaceSummaryChart = echarts.init(interfaceSummaryChartRef.value)
+    interfaceSummaryChart.setOption(interfaceSummaryOption)
+  } catch {}
+}
+</script>