Эх сурвалжийг харах

feature 上报分析&财务-佣金统计的接口对接

ylong 6 сар өмнө
parent
commit
3a171ecb4e

+ 85 - 0
src/views/finance/commission/commission-detail.vue

@@ -0,0 +1,85 @@
+<template>
+    <ele-modal
+        form
+        :width="1200"
+        v-model="visible"
+        title="佣金获取详情"
+        @open="handleOpen"
+    >
+        <common-table
+            ref="pageRef"
+            :pageConfig="pageConfig"
+            :columns="columns"
+            :tools="false"
+        />
+        <template #footer>
+            <el-button @click="handleCancel">关闭</el-button>
+        </template>
+    </ele-modal>
+</template>
+
+<script setup>
+import { ref, reactive, nextTick, watch } from 'vue';
+import CommonTable from '@/components/CommonPage/CommonTable.vue';
+
+const props = defineProps({
+    modelValue: {
+        type: Boolean,
+        default: false
+    },
+    rowData: {
+        type: Object,
+        default: null
+    }
+});
+
+const emit = defineEmits(['update:modelValue']);
+
+const pageRef = ref(null);
+
+/** 弹窗是否打开 */
+const visible = ref(false);
+
+// 监听modelValue变化
+watch(() => props.modelValue, (newVal) => {
+    visible.value = newVal;
+});
+
+// 监听visible变化
+watch(visible, (newVal) => {
+    emit('update:modelValue', newVal);
+});
+
+/** 关闭弹窗 */
+const handleCancel = () => {
+    visible.value = false;
+};
+
+/** 弹窗打开事件 */
+const handleOpen = () => {
+    if (props.rowData && props.rowData.beInviteUserId) {
+        pageConfig.params.inviteUserId = props.rowData.beInviteUserId;
+        nextTick(() => {
+            pageRef.value?.reload();
+        });
+    }
+};
+
+/** 表格列配置 */
+const columns = ref([
+    { label: "UID", prop: "beInviteUserId", align: "center", width: 120 },
+    { label: "昵称", prop: "nickName", align: "center", minWidth: 120 },
+    { label: "联系方式", prop: "mobile", align: "center", minWidth: 140 },
+    { label: "邀请时间", prop: "createTime", align: "center", width: 180 },
+    { label: "获得佣金", prop: "settlementMoney", align: "center", width: 120 },
+]);
+
+const pageConfig = reactive({
+    pageUrl: '/user/userInviteLog/myInviteLog',
+    fileName: '佣金详情',
+    cacheKey: 'commission-detail-table',
+    params: {
+        inviteUserId: null
+    }
+});
+</script>

+ 153 - 94
src/views/finance/commission/index.vue

@@ -1,105 +1,164 @@
 <template>
-  <ele-page flex-table>
-    <page-search @search="reload"></page-search>
-
-    <common-table ref="pageRef" :pageConfig="pageConfig" :columns="columns">
-      <template #toolbar>
-        <div class="flex items-center mb-4">
-          <el-statistic
-            :value="693700"
-            title="累计佣金金额"
-            value-style="font-size:30px"
-            class="mr-10"
-          ></el-statistic>
-          <el-statistic
-            :value="693700"
-            title="待结算佣金"
-            value-style="font-size:30px"
-            class="mr-10"
-          ></el-statistic>
-          <el-statistic
-            :value="693700"
-            title="已结算佣金"
-            value-style="font-size:30px"
-            class="mr-10"
-          ></el-statistic>
-          <el-statistic
-            :value="693700"
-            title="已失效佣金"
-            value-style="font-size:30px"
-            class="mr-10"
-          ></el-statistic>
-        </div>
-
-        <div class="common-title mb-4">交易记录</div>
-
-        <el-radio-group @change="handleStatusChange" v-model="useStatus">
-          <el-radio-button label="全部" value="1" />
-          <el-radio-button label="待结算" value="2" />
-          <el-radio-button label="已结算" value="3" />
-        </el-radio-group>
-      </template>
-    </common-table>
-  </ele-page>
+    <ele-page flex-table>
+        <page-search @search="reload" :status="useStatus"></page-search>
+
+        <common-table ref="pageRef" :pageConfig="pageConfig" :columns="columns">
+            <template #toolbar>
+                <div class="flex items-center mb-4">
+                    <el-statistic
+                        :value="statistics.totalMoney"
+                        title="累计佣金金额"
+                        :precision="2"
+                        value-style="font-size:30px"
+                        class="mr-20"
+                    ></el-statistic>
+                    <el-statistic
+                        :value="statistics.waitSettlementMoney"
+                        title="待结算佣金"
+                        :precision="2"
+                        value-style="font-size:30px"
+                        class="mr-20"
+                    ></el-statistic>
+                    <el-statistic
+                        :value="statistics.settlementMoney"
+                        title="已结算佣金"
+                        :precision="2"
+                        value-style="font-size:30px"
+                        class="mr-20"
+                    ></el-statistic>
+                    <el-statistic
+                        :value="statistics.arrivedMoney"
+                        title="已到账佣金"
+                        :precision="2"
+                        value-style="font-size:30px"
+                        class="mr-20"
+                    ></el-statistic>
+                    <el-statistic
+                        :value="statistics.invalidMoney"
+                        title="已失效佣金"
+                        :precision="2"
+                        value-style="font-size:30px"
+                        class="mr-20"
+                    ></el-statistic>
+                </div>
+
+                <div class="common-title mb-4">交易记录</div>
+
+                <el-radio-group
+                    @change="handleStatusChange"
+                    v-model="useStatus"
+                >
+                    <el-radio-button label="全部" value="" />
+                    <el-radio-button label="待结算" value="1" />
+                    <el-radio-button label="已结算" value="2" />
+                    <el-radio-button label="已到账" value="3" />
+                    <el-radio-button label="已作废" value="4" />
+                </el-radio-group>
+            </template>
+
+            <template #status="{ row }">
+                {{ settlementStatus.find((d) => d.value == row.status)?.label }}
+            </template>
+
+            <template #orderId="{ row }">
+                <el-button type="primary" link @click="handleOrderId(row)">{{
+                    row.beInviteOrderId
+                }}</el-button>
+            </template>
+        </common-table>
+
+        <commissionDetail v-model="commissionDetailVisible" :rowData="currentRowData" />
+    </ele-page>
 </template>
 
 <script setup>
-  import { ref, reactive } from 'vue';
-  import CommonTable from '@/components/CommonPage/CommonTable.vue';
-  import pageSearch from '@/views/finance/withdrawal/components/page-search.vue';
-  import { useDictData } from '@/utils/use-dict-data';
-
-  defineOptions({ name: 'withdrawal' });
-  const [useStatusDicts] = useDictData(['use_status']);
-
-  const useStatus = ref('1');
-  function handleStatusChange(value) {
-    pageRef.value.reload({ useStatus: value });
-  }
-
-  /** 表格列配置 */
-  const columns = ref([
-    { label: '交易时间', prop: 'createTime', align: 'center', width: 180 },
-    { label: '用户UID', prop: 'uid', align: 'center', minWidth: 140 },
-    {
-      label: '支付单号/流水号',
-      prop: 'paymentCode',
-      align: 'center',
-      minWidth: 160
-    },
-    { label: '对方账户', prop: 'addressDetail', align: 'center' },
-    { label: '结算金额', prop: 'money', align: 'center' },
-    {
-      label: '交易状态',
-      prop: 'useStatus',
-      align: 'center',
-      formatter: (row) =>
-        useStatusDicts.value.find((d) => d.dictValue == row.useStatus)
-          ?.dictLabel
-    },
+import { ref, reactive, onMounted, nextTick } from "vue";
+import CommonTable from "@/components/CommonPage/CommonTable.vue";
+import pageSearch from "./page-search.vue";
+import request from "@/utils/request";
+import CommissionDetail from "./commission-detail.vue";
+
+defineOptions({ name: "commission" });
+
+// 添加统计数据的响应式对象
+const statistics = reactive({
+    totalMoney: 0,
+    waitSettlementMoney: 0,
+    settlementMoney: 0,
+    arrivedMoney: 0,
+    invalidMoney: 0,
+});
+
+// 获取统计数据
+async function fetchStatistics() {
+    try {
+        const res = await request.get("/user/userInviteLog/sum");
+        if (res.data.code === 200) {
+            Object.assign(statistics, res.data.data);
+        }
+    } catch (error) {
+        console.error("获取统计数据失败:", error);
+    }
+}
+
+const commissionDetailVisible = ref(false);
+const currentRowData = ref(null);
+const handleOrderId = (row) => {
+    currentRowData.value = row;
+    commissionDetailVisible.value = true;
+};
+
+onMounted(() => {
+    fetchStatistics();
+});
+
+const useStatus = ref("");
+function handleStatusChange(value) {
+    pageRef.value.reload({ status: value });
+}
+
+//结算状态数组
+const settlementStatus = ref([
+    { label: "待结算", value: 1 },
+    { label: "已结算", value: 2 },
+    { label: "已到账", value: 3 },
+    { label: "已作废", value: 4 },
+]);
+
+/** 表格列配置 */
+const columns = ref([
+    { label: "交易时间", prop: "settlementTime", align: "center", width: 180 },
+    { label: "合伙人姓名", prop: "partnerName", align: "center" },
+    { label: "合伙人手机号", prop: "partnerMobile", align: "center" },
+    { label: "下单人姓名", prop: "orderName", align: "center" },
+    { label: "下单人手机号", prop: "orderMobile", align: "center" },
+    { label: "订单金额", prop: "orderExpectMoney", align: "center" },
+    { label: "结算金额", prop: "orderFinalMoney", align: "center" },
+    { label: "预估收入", prop: "expectSettlementMoney", align: "center" },
+    { label: "结算收入", prop: "settlementMoney", align: "center" },
+    { label: "交易状态", prop: "status", align: "center", slot: "status" },
     {
-      label: '交易类型',
-      prop: 'paymentType',
-      align: 'center',
-      formatter: (row) =>
-        useStatusDicts.value.find((d) => d.dictValue == row.useStatus)
-          ?.dictLabel
+        label: "订单编号",
+        prop: "beInviteOrderId",
+        align: "center",
+        slot: "orderId",
     },
-    { label: '订单编号', prop: 'code', align: 'center' }
-  ]);
+]);
 
-  /** 页面组件实例 */
-  const pageRef = ref(null);
+/** 页面组件实例 */
+const pageRef = ref(null);
 
-  const pageConfig = reactive({
-    pageUrl: '/baseinfo/godown/pagelist',
-    exportUrl: '/baseinfo/godown/export',
-    fileName: '佣金记录',
-    cacheKey: 'commissionTable'
-  });
+const pageConfig = reactive({
+    pageUrl: "/user/userInviteLog/pageList",
+    fileName: "佣金记录",
+    cacheKey: "commissionTable",
+    params: {
+        status: useStatus.value,
+    },
+});
 
-  //刷新表格
-  function reload(where) {
+//刷新表格
+function reload(where) {
     pageRef.value?.reload(where);
-  }
+}
 </script>

+ 71 - 0
src/views/finance/commission/page-search.vue

@@ -0,0 +1,71 @@
+<!-- 搜索表单 -->
+<template>
+    <ele-card :body-style="{ paddingBottom: '8px' }">
+        <ProSearch
+            :items="formItems"
+            ref="searchRef"
+            @search="search"
+            :initKeys="initKeys"
+        ></ProSearch>
+    </ele-card>
+</template>
+
+<script setup>
+import { reactive, ref, defineEmits, watch, getCurrentInstance } from "vue";
+import ProSearch from "@/components/CommonPage/ProSearch2.vue";
+
+const emit = defineEmits(["search"]);
+
+const props = defineProps({
+    status: {
+        type: String,
+        default: ''
+    }
+});
+
+const formItems = reactive([
+    { type: "input", label: "合伙人姓名", prop: "partnerName" },
+    {
+        type: "select",
+        label: "状态",
+        prop: "status",
+        options: [
+            // 1-未结算 2-已结算 3-已到账 4-已作废
+            { label: "待结算", value: 1 },
+            { label: "已结算", value: 2 },
+            { label: "已到账", value: 3 },
+            { label: "已作废", value: 4 },
+        ],
+    },
+    {
+        type: "daterange",
+        label: "时间",
+        prop: "timeRange",
+        props: {
+            valueFormat: "YYYY-MM-DD",
+            format: "YYYY-MM-DD",
+            startPlaceholder: "开始日期",
+            endPlaceholder: "结束日期",
+            onChange: (value) => {
+                initKeys.createTimeStart = value ? value[0] : "";
+                initKeys.createTimeEnd = value ? value[1] : "";
+                searchRef.value?.setData(initKeys);
+            },
+        },
+    },
+]);
+
+const initKeys = reactive({
+    createTimeStart: "",
+    createTimeEnd: "",
+    partnerName: "",
+    status: "",
+});
+
+const searchRef = ref(null);
+/** 搜索 */
+const search = (data) => {
+	delete data.timeRange;
+    emit("search", { ...data });
+};
+</script>

+ 68 - 44
src/views/optimization/reportAnalysis/components/pie-chart.vue

@@ -2,50 +2,74 @@
   <v-chart ref="saleChartRef" style="height: 420px" :option="options" />
 </template>
 <script setup>
-  import VChart from 'vue-echarts';
-  import { use } from 'echarts/core';
-  import { CanvasRenderer } from 'echarts/renderers';
-  import { PieChart } from 'echarts/charts';
-  import {
-    GridComponent,
-    TooltipComponent,
-    LegendComponent,
-    ToolboxComponent
-  } from 'echarts/components';
+import VChart from 'vue-echarts';
+import { use } from 'echarts/core';
+import { CanvasRenderer } from 'echarts/renderers';
+import { PieChart } from 'echarts/charts';
+import {
+  GridComponent,
+  TooltipComponent,
+  LegendComponent,
+  ToolboxComponent
+} from 'echarts/components';
+import { ref, reactive, defineExpose } from 'vue';
+import request from '@/utils/request';
 
-  // 按需加载echarts
-  use([
-    CanvasRenderer,
-    PieChart,
-    GridComponent,
-    TooltipComponent,
-    LegendComponent
-  ]);
+// 按需加载echarts
+use([
+  CanvasRenderer,
+  PieChart,
+  GridComponent,
+  TooltipComponent,
+  LegendComponent
+]);
 
-  const options = reactive({
-    legend: {
-      top: 'bottom'
-    },
-    series: [
-      {
-        name: 'Nightingale Chart',
-        type: 'pie',
-        radius: [50, 150],
-        center: ['50%', '50%'],
-        roseType: 'area',
-        itemStyle: {
-          borderRadius: 8
-        },
-        data: [
-          { value: 40, name: '快递不取件' },
-          { value: 38, name: '快递不上门' },
-          { value: 32, name: '时间不合适' },
-          { value: 30, name: '发现更贵的平台' },
-          { value: 28, name: '不想卖了' },
-          { value: 26, name: '信息填写错误' },
-          { value: 22, name: '以上都不是' }
-        ]
-      }
-    ]
-  });
+const options = reactive({
+  title: {
+    text: '上报分析-理由比例',
+    left: 'center',
+    top: 10,
+    textStyle: {
+      fontSize: 16,
+      fontWeight: 'bold'
+    }
+  },
+  legend: {
+    top: 'bottom'
+  },
+  series: [
+    {
+      name: '理由比例分布',
+      type: 'pie',
+      radius: [50, 150],
+      center: ['50%', '50%'],
+      roseType: 'area',
+      itemStyle: {
+        borderRadius: 8
+      },
+      label: {
+        formatter: '{b}: {c} ({d}%)'
+      },
+      data: []
+    }
+  ]
+});
+
+// 更新图表数据
+const updateChartData = (data) => {
+  if (!data || !Array.isArray(data)) return;
+
+  // 将接口返回的数据转换为图表所需格式
+  const chartData = data.map(item => ({
+    value: item.count,
+    name: item.reason
+  }));
+
+  options.series[0].data = chartData;
+};
+
+// 暴露更新方法给父组件使用
+defineExpose({
+  updateChartData
+});
 </script>

+ 25 - 13
src/views/optimization/reportAnalysis/components/rank-list.vue

@@ -9,23 +9,35 @@
 </template>
 
 <script setup>
-  import SimpleTable from '@/components/CommonPage/SimpleTable.vue';
+  import { reactive, defineExpose } from 'vue';
   import request from '@/utils/request';
 
   const columns = reactive([
     { label: '排行榜', prop: 'rank', align: 'center' },
-    { label: '上报地区', prop: 'area', align: 'center' },
-    { label: '上报数量', prop: 'number', align: 'center' }
+    { label: '上报地区', prop: 'areaName', align: 'center' },
+    { label: '上报数量', prop: 'cancelOrderCount', align: 'center' }
   ]);
 
-  const dataList = reactive([
-    { rank: 1, area: '北京', number: 10 },
-    { rank: 2, area: '上海', number: 9 },
-    { rank: 3, area: '广州', number: 8 },
-    { rank: 4, area: '深圳', number: 7 },
-    { rank: 5, area: '成都', number: 6 },
-    { rank: 6, area: '杭州', number: 5 },
-    { rank: 7, area: '武汉', number: 4 },
-    { rank: 8, area: '西安', number: 3 },
-  ]);
+  const dataList = reactive([]);
+
+  // 更新排行榜数据
+  const updateRankData = (data) => {
+    if (!data || !Array.isArray(data)) return;
+    
+    // 清空现有数据
+    dataList.length = 0;
+    
+    // 添加新数据,同时设置排名
+    data.forEach((item, index) => {
+      dataList.push({
+        ...item,
+        rank: index + 1
+      });
+    });
+  };
+
+  // 暴露更新方法给父组件使用
+  defineExpose({
+    updateRankData
+  });
 </script>

+ 46 - 15
src/views/optimization/reportAnalysis/index.vue

@@ -8,8 +8,8 @@
     >
       <template #toolbar>
         <div class="flex mb-6">
-          <pieChart class="flex-1"></pieChart>
-          <rank-list></rank-list>
+          <pieChart ref="pieChartRef" class="flex-1"></pieChart>
+          <rank-list ref="rankListRef"></rank-list>
         </div>
       </template>
     </common-table>
@@ -17,34 +17,65 @@
 </template>
 
 <script setup>
-  import { ref, reactive } from 'vue';
+  import { ref, reactive, onMounted } from 'vue';
   import CommonTable from '@/components/CommonPage/CommonTable.vue';
-  import pieChart from '@/views/optimization/orderAnalysis/components/pie-chart.vue';
-  import rankList from '@/views/optimization/orderAnalysis/components/rank-list.vue';
-  import { useDictData } from '@/utils/use-dict-data';
+  import pieChart from '@/views/optimization/reportAnalysis/components/pie-chart.vue';
+  import rankList from '@/views/optimization/reportAnalysis/components/rank-list.vue';
+  import request from '@/utils/request';
+  import { ElMessage } from 'element-plus';
 
-  defineOptions({ name: 'orderCancelAnalysis' });
+  defineOptions({ name: 'reportAnalysis' });
+
+  // 图表组件实例
+  const pieChartRef = ref(null);
+  const rankListRef = ref(null);
 
   /** 表格列配置 */
   const columns = ref([
-    { label: '订单编号', prop: 'orderCode', align: 'center' },
-    { label: '用户UID', prop: 'uid', align: 'center' },
-    { label: '取消原因', prop: 'cancelReason', align: 'center', minWidth: 200 },
-    { label: '取消时间', prop: 'createTime', align: 'center' }
+    { label: '订单编号', prop: 'orderId', align: 'center' },
+    { label: '用户UID', prop: 'userId', align: 'center' },
+    { label: '取消原因', prop: 'reason', align: 'center', minWidth: 200 },
+    { label: '取消时间', prop: 'cancelTime', align: 'center' }
   ]);
 
   /** 页面组件实例 */
   const pageRef = ref(null);
 
   const pageConfig = reactive({
-    pageUrl: '/baseinfo/godown/pagelist',
-    exportUrl: '/baseinfo/godown/export',
-    fileName: '订单取消分析',
-    cacheKey: 'orderCancelAnalysisTable'
+    pageUrl: '/order/recycleOrderStatistic/cancel/list',
+    fileName: '上报分析',
+    cacheKey: 'reportAnalysisTable'
   });
 
+  const fetchData = async () => {
+    try {
+      // 获取理由比例数据(饼图)
+      const reasonResponse = await request.get('/order/recycleOrderStatistic/cancel/reasonStat');
+      if (reasonResponse.data && reasonResponse.data.data) {
+        pieChartRef.value?.updateChartData(reasonResponse.data.data);
+      }
+      
+      // 获取地区榜单数据(排行榜)
+      const areaResponse = await request.get('/order/recycleOrderStatistic/cancel/areaRank');
+      if (areaResponse.data && areaResponse.data.data) {
+        rankListRef.value?.updateRankData(areaResponse.data.data);
+      }
+      
+      // 刷新表格
+      reload();
+    } catch (error) {
+      ElMessage.error('获取数据失败');
+      console.error('Failed to fetch statistic data:', error);
+    }
+  };
+
   //刷新表格
   function reload(where) {
     pageRef.value?.reload(where);
   }
+
+  onMounted(() => {
+    // 默认加载数据
+    fetchData();
+  });
 </script>