|
|
@@ -0,0 +1,390 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <div
|
|
|
+ class="mt-[30px] mb-[20px] bg-[#fff] rounded-[10px] p-[15px] flex justify-between"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ v-for="item in tabList"
|
|
|
+ :key="item.value"
|
|
|
+ @click="
|
|
|
+ () => {
|
|
|
+ searchForm.type = item.value;
|
|
|
+ refresh();
|
|
|
+ }
|
|
|
+ "
|
|
|
+ class="px-[20px] py-[10px] rounded-[5px] cursor-pointer transition-all duration-300"
|
|
|
+ :class="{ 'bg-[#4466ee] text-white': searchForm.type == item.value }"
|
|
|
+ >
|
|
|
+ <span class="text-[16px] flex items-center"
|
|
|
+ ><sa-icon :icon="item.icon" class="text-[16px] mr-[5px]" />{{
|
|
|
+ item.label
|
|
|
+ }}</span
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <div class="mb-[15px]">
|
|
|
+ <sa-icon
|
|
|
+ :icon="tabList.find((item) => item.value === activeTab).icon"
|
|
|
+ class="text-[25px] mr-[5px]"
|
|
|
+ />
|
|
|
+ <span class="text-[25px] font-bold"
|
|
|
+ >{{
|
|
|
+ tabList.find((item) => item.value === activeTab).label
|
|
|
+ }}对比</span
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="grid grid-cols-3 gap-[15px]">
|
|
|
+ <a-card
|
|
|
+ class="text-center rounded-[10px] hover:translate-y-[-5px] transition-all duration-300 cursor-pointer p-[10px] border-none"
|
|
|
+ >
|
|
|
+ <div class="text-[#6c757d] text-[18px] font-bold">
|
|
|
+ {{ dictOptions[searchForm.type] }}下单量
|
|
|
+ </div>
|
|
|
+ <a-statistic
|
|
|
+ class="my-[5px]"
|
|
|
+ :value="orderNum || 0"
|
|
|
+ :value-from="0"
|
|
|
+ animation
|
|
|
+ show-group-separator
|
|
|
+ value-style="color: #4361ee; font-size: 44px; font-weight: bold; line-height: 1.2;"
|
|
|
+ />
|
|
|
+
|
|
|
+ <div class="text-[15px] font-bold">
|
|
|
+ <a-statistic
|
|
|
+ :value="comparison.changes.orderChange.percentageText"
|
|
|
+ :precision="2"
|
|
|
+ :value-from="0"
|
|
|
+ :start="start"
|
|
|
+ animation
|
|
|
+ value-style="color: #09d6a1; font-size: 15px; font-weight: bold; line-height: 1.2;"
|
|
|
+ >
|
|
|
+ <template #prefix>
|
|
|
+ <icon-arrow-rise
|
|
|
+ v-if="comparison.changes.orderChange.trend === 'up'"
|
|
|
+ />
|
|
|
+ <icon-arrow-fall v-else />
|
|
|
+ </template>
|
|
|
+ <template #suffix
|
|
|
+ >% {{ comparison.changes.orderChange.label }}</template
|
|
|
+ >
|
|
|
+ </a-statistic>
|
|
|
+ </div>
|
|
|
+ </a-card>
|
|
|
+ <a-card
|
|
|
+ class="text-center rounded-[10px] hover:translate-y-[-5px] transition-all duration-300 cursor-pointer p-[10px] border-none"
|
|
|
+ >
|
|
|
+ <div class="text-[#6c757d] text-[18px] font-bold">
|
|
|
+ {{ dictOptions[searchForm.type] }}支付量
|
|
|
+ </div>
|
|
|
+ <a-statistic
|
|
|
+ class="my-[5px]"
|
|
|
+ :value="payNum || 0"
|
|
|
+ :value-from="0"
|
|
|
+ animation
|
|
|
+ show-group-separator
|
|
|
+ value-style="color: #4361ee; font-size: 44px; font-weight: bold; line-height: 1.2;"
|
|
|
+ />
|
|
|
+
|
|
|
+ <div class="text-[15px] font-bold">
|
|
|
+ <a-statistic
|
|
|
+ :value="comparison.changes.payChange.percentageText"
|
|
|
+ :precision="2"
|
|
|
+ :value-from="0"
|
|
|
+ :start="start"
|
|
|
+ animation
|
|
|
+ value-style="color: #09d6a1; font-size: 15px; font-weight: bold; line-height: 1.2;"
|
|
|
+ >
|
|
|
+ <template #prefix>
|
|
|
+ <icon-arrow-rise
|
|
|
+ v-if="comparison.changes.orderChange.trend === 'up'"
|
|
|
+ />
|
|
|
+ <icon-arrow-fall v-else />
|
|
|
+ </template>
|
|
|
+ <template #suffix
|
|
|
+ >% {{ comparison.changes.payChange.label }}</template
|
|
|
+ >
|
|
|
+ </a-statistic>
|
|
|
+ </div>
|
|
|
+ </a-card>
|
|
|
+ <a-card
|
|
|
+ class="text-center rounded-[10px] hover:translate-y-[-5px] transition-all duration-300 cursor-pointer p-[10px] border-none"
|
|
|
+ >
|
|
|
+ <div class="text-[#6c757d] text-[18px] font-bold">
|
|
|
+ {{ dictOptions[searchForm.type] }}支付成功率
|
|
|
+ </div>
|
|
|
+ <a-statistic
|
|
|
+ class="my-[5px]"
|
|
|
+ :value="paySuccessRate * 100 || 0"
|
|
|
+ :value-from="0"
|
|
|
+ animation
|
|
|
+ show-group-separator
|
|
|
+ value-style="color: #4361ee; font-size: 44px; font-weight: bold; line-height: 1.2;"
|
|
|
+ >
|
|
|
+ <template #suffix>%</template>
|
|
|
+ </a-statistic>
|
|
|
+
|
|
|
+ <div class="text-[15px] font-bold">
|
|
|
+ <a-statistic
|
|
|
+ :value="comparison.changes.rateChange.percentageText"
|
|
|
+ :precision="2"
|
|
|
+ :value-from="0"
|
|
|
+ :start="start"
|
|
|
+ animation
|
|
|
+ value-style="color: #f53f3f; font-size: 15px; font-weight: bold; line-height: 1.2;"
|
|
|
+ >
|
|
|
+ <template #prefix>
|
|
|
+ <icon-arrow-rise
|
|
|
+ v-if="comparison.changes.orderChange.trend === 'up'"
|
|
|
+ />
|
|
|
+ <icon-arrow-fall v-else />
|
|
|
+ </template>
|
|
|
+ <template #suffix
|
|
|
+ >% {{ comparison.changes.orderChange.label }}</template
|
|
|
+ >
|
|
|
+ </a-statistic>
|
|
|
+ </div>
|
|
|
+ </a-card>
|
|
|
+ </div>
|
|
|
+ <div class="mt-[24px] gap-[15px] flex">
|
|
|
+ <a-card class="w-[70%]" title="今日 VS 昨日支付成功率对比">
|
|
|
+ <sa-chart :option="lineChartOption" height="300px" />
|
|
|
+ </a-card>
|
|
|
+ <a-card class="w-[30%]" title="支付渠道下单比例">
|
|
|
+ <!-- <sa-chart :option="lineChartOption" height="300px" /> -->
|
|
|
+ </a-card>
|
|
|
+ </div>
|
|
|
+ <a-card title="今日 vs 昨日详细数据" class="mt-[24px]">
|
|
|
+ <sa-table
|
|
|
+ ref="crudRef"
|
|
|
+ :searchForm="searchForm"
|
|
|
+ :columns="columns"
|
|
|
+ :options="options"
|
|
|
+ />
|
|
|
+ </a-card>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { onMounted, reactive, ref, watch } from "vue";
|
|
|
+import SaIcon from "@/components/sa-icon/index.vue";
|
|
|
+import SaChart from "@/components/sa-chart/index.vue";
|
|
|
+import api from "../../api/gameLog/analyse";
|
|
|
+
|
|
|
+const crudRef = ref();
|
|
|
+const orderNum = ref(0);
|
|
|
+const payNum = ref(0);
|
|
|
+const paySuccessRate = ref(0);
|
|
|
+const dictOptions = ref({
|
|
|
+ day: "今日",
|
|
|
+ week: "本周",
|
|
|
+ month: "本月",
|
|
|
+});
|
|
|
+const searchForm = ref({
|
|
|
+ type: "day",
|
|
|
+});
|
|
|
+
|
|
|
+const lineChartOption = ref({});
|
|
|
+const chartOption = ref({});
|
|
|
+const comparison = ref({
|
|
|
+ changes: {
|
|
|
+ orderChange: {
|
|
|
+ percentage: 0,
|
|
|
+ trend: "up",
|
|
|
+ value: 0,
|
|
|
+ percentageText: "",
|
|
|
+ icon: "icon-arrow-rise",
|
|
|
+ label: "",
|
|
|
+ },
|
|
|
+ payChange: {
|
|
|
+ percentage: 0,
|
|
|
+ trend: "up",
|
|
|
+ value: 0,
|
|
|
+ percentageText: "",
|
|
|
+ icon: "icon-arrow-rise",
|
|
|
+ label: "",
|
|
|
+ },
|
|
|
+ rateChange: {
|
|
|
+ percentage: 0,
|
|
|
+ trend: "up",
|
|
|
+ percentageText: "",
|
|
|
+ value: 0,
|
|
|
+ icon: "icon-arrow-rise",
|
|
|
+ label: "",
|
|
|
+ },
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
+const options = reactive({
|
|
|
+ api: api.getPaySuccessRate,
|
|
|
+ pk: "time",
|
|
|
+ showSort: false,
|
|
|
+ operationColumn: false,
|
|
|
+ showSearch: false,
|
|
|
+ pageSimple: true,
|
|
|
+});
|
|
|
+const columns = reactive([
|
|
|
+ {
|
|
|
+ title: "时段",
|
|
|
+ dataIndex: "time",
|
|
|
+ width: 100,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "今日下单量",
|
|
|
+ dataIndex: "todayOrderNum",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "今日支付量",
|
|
|
+ dataIndex: "todayPayNum",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "今日成功率",
|
|
|
+ dataIndex: "todayPaySuccessRate",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "昨日下单量",
|
|
|
+ dataIndex: "yesterdayOrderNum",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "昨日支付量",
|
|
|
+ dataIndex: "yesterdayPayNum",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "昨日成功率",
|
|
|
+ dataIndex: "yesterdayPaySuccessRate",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "成功率变化",
|
|
|
+ dataIndex: "successRateChange",
|
|
|
+ },
|
|
|
+]);
|
|
|
+
|
|
|
+const tabList = ref([
|
|
|
+ {
|
|
|
+ label: "今日 vs 昨日",
|
|
|
+ value: "day",
|
|
|
+ icon: "IconSun",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: "本周 vs 上周",
|
|
|
+ value: "week",
|
|
|
+ icon: "IconCalendar",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: "本月 vs 上月",
|
|
|
+ value: "month",
|
|
|
+ icon: "IconComputer",
|
|
|
+ },
|
|
|
+]);
|
|
|
+const activeTab = ref("day");
|
|
|
+
|
|
|
+// 请求支付成功率
|
|
|
+const getPaySuccessRate = async () => {
|
|
|
+ const res = await api.getPaySuccessRate({
|
|
|
+ type: searchForm.value.type,
|
|
|
+ });
|
|
|
+ let data = res.data;
|
|
|
+ orderNum.value = data.orderNum;
|
|
|
+ payNum.value = data.payNum;
|
|
|
+ paySuccessRate.value = data.paySuccessRate;
|
|
|
+ chartOption.value = {
|
|
|
+ legend: {
|
|
|
+ data: ["今日支付成功率", "昨日支付成功率"],
|
|
|
+ itemGap: 5,
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ left: "1%",
|
|
|
+ right: "10%",
|
|
|
+ containLabel: true,
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: "category",
|
|
|
+ data: data.xAxisData,
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: "value",
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: "今日支付成功率",
|
|
|
+ type: "bar",
|
|
|
+ data: data.PaySuccessRateArr,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "昨日支付成功率",
|
|
|
+ type: "bar",
|
|
|
+ data: data.OldPaySuccessRate,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ tooltip: {
|
|
|
+ trigger: "axis",
|
|
|
+ },
|
|
|
+ };
|
|
|
+ lineChartOption.value = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: "axis",
|
|
|
+ axisPointer: {
|
|
|
+ type: "cross",
|
|
|
+ label: {
|
|
|
+ backgroundColor: "#6a7985",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ data: ["今日", "昨日"],
|
|
|
+ itemGap: 5,
|
|
|
+ },
|
|
|
+ toolbox: {
|
|
|
+ feature: {
|
|
|
+ saveAsImage: {},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ xAxis: [
|
|
|
+ {
|
|
|
+ type: "category",
|
|
|
+ boundaryGap: false,
|
|
|
+ data: data.xAxisData,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ yAxis: [
|
|
|
+ {
|
|
|
+ type: "value",
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: "今日",
|
|
|
+ type: "line",
|
|
|
+ areaStyle: {},
|
|
|
+ emphasis: {
|
|
|
+ focus: "series",
|
|
|
+ },
|
|
|
+ data: data.PaySuccessRateArr,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "昨日",
|
|
|
+ type: "line",
|
|
|
+ areaStyle: {},
|
|
|
+ emphasis: {
|
|
|
+ focus: "series",
|
|
|
+ },
|
|
|
+ data: data.OldPaySuccessRateArr,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+// SaTable 数据请求
|
|
|
+const refresh = async () => {
|
|
|
+ crudRef.value?.refresh();
|
|
|
+ getPaySuccessRate();
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ getPaySuccessRate();
|
|
|
+ refresh();
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped></style>
|