hbchen 1 жил өмнө
parent
commit
54e139d95e

+ 2 - 2
.env.development

@@ -1,5 +1,5 @@
 # 接口地址
-VITE_APP_API_URL="http://192.168.77.4:8619/api"
-# VITE_APP_API_URL="http://8.136.199.33:8619/api"
+# VITE_APP_API_URL="http://192.168.77.4:8619/api"
+VITE_APP_API_URL="http://8.136.199.33:8619/api"
 # crm系统地址
 VITE_CRM_SYSTEM_URL="https://rddptest.hzinsights.com/login"

+ 13 - 1
src/api/crm.js

@@ -16,7 +16,7 @@ export function getPermissionList(data) {
       params:data
   })
 }
-// 获取销售列表(包括禁用的销售)
+// 获取销售列表(包括禁用的销售) -- 已替换为 getSellerTeamList()
  /**
   * @param {
   * } data 
@@ -40,6 +40,18 @@ export function getSellerListNoG() {
      method:'get'
  })
 }
+// 获取详细的销售列表(包含小组以及禁用的小组)
+ /**
+  * @param {
+ * } data 
+ * @returns 
+ */
+export function getSellerTeamList() {
+ return request({
+     url:'/crm/company_seller/team_list',
+     method:'get'
+ })
+}
 
 // 根据合同编号搜索合同列表
  /**

+ 21 - 1
src/api/financialStatistics.js

@@ -99,4 +99,24 @@ export function setServiceAmount(data) {
         method:'post',
         data
     })
-}
+}
+
+// 业务收入金额统计
+ /**
+ * 获取业务收入金额
+ * @param data.start_date - 开始时间
+ * @param data.end_date - 结束时间
+ * @param data.is_export - 是否是导出 0-否 1-是
+ * @param data.list_param - 统计类型: 0-月度; 1-季度; 2-半年度;3-年度;4-月度累计
+ * @param data.company_type - 客户类型 0全部 1新客户 2老客户
+ * @param data.seller_ids - 销售ids
+ * @returns 
+ */
+ export function getIncomeChartData(data) {
+    return request({
+        url:'/census/invoice_payment/income/list',
+        method:'get',
+        params:data,
+        responseType:data.is_export==1?'blob':'text',
+    })
+   }

+ 58 - 0
src/components/echart/baseOptions.js

@@ -0,0 +1,58 @@
+export const baseOptions=()=>{
+  return {
+    title: {
+      text:'默认图表',
+      textStyle:{
+        fontSize:20,
+        fontWeight:500
+      },
+      left:'center'
+    },
+    legend:{
+      icon:'circle',
+      left:'center',
+      top:40,
+      textStyle:{
+        fontSize:14
+      },
+      itemStyle:{},
+      itemGap:34,
+      itemWidth:12
+    },
+    tooltip:{
+      textStyle:{
+        align:'left'
+      },
+      trigger:'axis'
+    },
+    grid:{
+      top:120,
+      bottom:60
+    },
+    color: ['#FF903E', '#FBE947'],
+    xAxis:{
+      type: 'category',
+      axisLabel: {
+        color: '#999', //更改坐标轴文字颜色
+        fontSize: 14, //更改坐标轴文字大小
+      }
+    },
+    yAxis: [{
+      name:'万元',
+      type: 'value',
+      nameGap:20,
+      splitNumber:10,
+      alignTicks:true,
+      nameTextStyle:{
+        align:'right',
+        color:'#999'
+      },
+      axisLabel:{
+        color:'#999',
+        formatter:(value)=>{
+          return Math.round(value*100)/1000000
+        }
+      }
+    }]
+  }
+}

+ 88 - 0
src/components/echart/index.vue

@@ -0,0 +1,88 @@
+<script setup>
+  import * as echarts from 'echarts/core';
+  // 引入柱状图图表,图表后缀都为 Chart
+  import { BarChart,LineChart } from 'echarts/charts';
+  // 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
+  import {
+    TitleComponent,
+    TooltipComponent,
+    GridComponent,
+    DatasetComponent,
+    LegendComponent
+  } from 'echarts/components';
+  // 标签自动布局、全局过渡动画等特性
+  import { LabelLayout, UniversalTransition } from 'echarts/features';
+  // 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
+  import { SVGRenderer } from 'echarts/renderers';
+
+  // 注册必须的组件
+  echarts.use([
+    TitleComponent,
+    TooltipComponent,
+    GridComponent,
+    DatasetComponent,
+    LegendComponent,
+    BarChart,
+    LineChart,
+    LabelLayout,
+    UniversalTransition,
+    SVGRenderer
+  ]);
+
+  const chartProp=defineProps({
+    options:{
+      type:Object,
+      require:true
+    },
+    title:{
+      type:String,
+      default:''
+    }
+  })
+  // 侧边导航栏收缩/展开值
+  const isCollapse=inject('isCollapse')
+
+  watch(()=>isCollapse.value,(value)=>{
+    setTimeout(()=>{
+      resizeChart()
+    },300)
+  })
+
+  const barChartRef = ref(null)
+  let barChart = null
+
+  const resizeChart=()=>{
+    barChart.resize();
+  }
+
+  const chartDraw=(option)=>{
+    // nextTick(()=>{
+      barChart.clear()
+      barChart.setOption(option)
+    // })
+  }
+
+  watch(()=>chartProp.options,(newVal)=>{
+    // console.log(newVal,'newVal');
+    if(!newVal.series) return 
+    chartDraw(newVal)
+  },{deep:true})
+
+  onMounted(()=>{
+    barChart = echarts.init(barChartRef.value)
+    window.addEventListener('resize', resizeChart);
+  })
+
+  onUnmounted(()=>{
+    barChart?.dispose()
+    window.removeEventListener('resize', resizeChart);
+  })
+</script>
+
+<template>
+    <div ref="barChartRef" style="height: 100%;"></div>
+</template>
+  
+<style lang="scss" scoped>
+  
+</style>

+ 3 - 1
src/layout/index.vue

@@ -7,6 +7,8 @@
   import {changePsdFirst} from '@/api/systemMana'
 
   const isCollapse = ref(false)
+  provide('isCollapse',isCollapse)
+  
   if (document.body.clientWidth <= 1500) {
     isCollapse.value = true;
   }
@@ -79,7 +81,7 @@
             <router-view v-slot="{ Component }">
             <!-- 使用transition 每个vue文件必须要有一个根节点 和vue2一样 -->
               <transition name="fade" mode="out-in">
-                  <component :is="Component" :key="$route.name" />
+                  <component :is="Component" :key="$route.name" ref="routerComponent"/>
               </transition>
             </router-view>
         </div>

+ 1 - 0
src/styles/main.scss

@@ -85,6 +85,7 @@ div::-webkit-scrollbar-corner {
   border: 1px solid $themeColor;
   font-size: 16px;
   width: 130px;
+  cursor: pointer;
 }
 // 主题按钮 - 大
 .main-button-large{

+ 32 - 6
src/utils/request.js

@@ -8,20 +8,42 @@ const request = axios.create({
 
 })
 
-let requestList=new Set()
+// 对于重复的post请求,只处理第一次请求,后续重复的请求在上一次返回之前都取消
+const postRequestList=new Set()
+
+
+// 需要储存每次请求对应的取消方法
+let lastGetRequestList=new Map()
+// 取消方法
+const removePreRequest=(Url,fun)=>{
+  console.log(lastGetRequestList);
+  if(lastGetRequestList.has(Url)){
+    // 取消上一次请求
+    lastGetRequestList.get(Url)(`${Url}---重复请求被中断`)
+    // 设置 新的取消方法
+    lastGetRequestList.set(Url,fun)
+  }
+}
 
 request.interceptors.request.use(
   config=>{
-    // console.log(config);
     config.headers['Authorization'] = localStorage.getItem('fsms_token') || ''
        // 不处理get请求
     if (config.method !== 'get') {
       // 利用axios cancelToken 先取消请求
       config.cancelToken = new axios.CancelToken(e => {
-        // console.log(e, config, 'CancelToken')
         // 在这里取消重复请求,上个请求未完成时,相同的请求不会再次执行
-        requestList.has(`${config.url}`) ? e(`${config.url}---重复请求被中断`) : requestList.add(`${config.url}`)
+        postRequestList.has(config.url) ? e(`${config.url}---重复请求被中断`) : postRequestList.add(config.url)
       })
+    }else if(config.params?.takeLastRequestResult){
+      // 对于get请求中的参数有 takeLastRequestResult 且值为真 进行特殊处理,若重名 请不要更改这个名字
+      config.cancelToken = new axios.CancelToken((c)=>{
+        lastGetRequestList.has(config.url) ? removePreRequest(config.url,c) : lastGetRequestList.set(config.url,c)
+      });
+      // 去除参数中的 takeLastRequestResult
+      if(config.params){
+        config.params.takeLastRequestResult=undefined
+      }
     }
     return config
   },
@@ -33,7 +55,8 @@ request.interceptors.request.use(
 
 request.interceptors.response.use(
   res=>{
-    requestList.delete(`${res.config.url}`)
+    postRequestList.delete(`${res.config.url}`)
+    lastGetRequestList.delete(`${res.config.url}`)
     if(res.request.responseType==='blob' || res.request.responseType==='Blob'){
        // 字节流
        if(res.status === 200){
@@ -64,10 +87,13 @@ request.interceptors.response.use(
     }
   },
   err=>{
-    requestList.clear()
     const {message} = err
     console.log(message,'--错误信息');
     if(!err.__CANCEL__){
+      // 清除
+      postRequestList.clear()
+      lastGetRequestList.clear()
+
       // 多次点击由CancelToken取消的请求不给错误提示
       ElMessage({
         message: message || '网络异常',

+ 20 - 116
src/views/dashboard/index.vue

@@ -1,136 +1,37 @@
 <script setup>
 
   import {getStatistic} from '@/api/dashboard'
-  import * as echarts from 'echarts/core';
-  // 引入柱状图图表,图表后缀都为 Chart
-  import { BarChart } from 'echarts/charts';
-  // 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
-  import {
-    TitleComponent,
-    TooltipComponent,
-    GridComponent,
-    DatasetComponent,
-    LegendComponent
-  } from 'echarts/components';
-  // 标签自动布局、全局过渡动画等特性
-  import { LabelLayout, UniversalTransition } from 'echarts/features';
-  // 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
-  import { CanvasRenderer } from 'echarts/renderers';
-
+  import chart from '@/components/echart/index.vue'
   import {useRouter} from 'vue-router'
-
-  // 注册必须的组件
-  echarts.use([
-    TitleComponent,
-    TooltipComponent,
-    GridComponent,
-    DatasetComponent,
-    LegendComponent,
-    BarChart,
-    LabelLayout,
-    UniversalTransition,
-    CanvasRenderer
-  ]);
+  import {baseOptions} from '@/components/echart/baseOptions'
   
   const router = useRouter()
-
-  const barChartRef = ref(null)
-  let barChart = null
-
+  const basicOptions=baseOptions()
+  const chartOptions=ref()
+  
   const paymentNavigator=()=>{
     // 去往商品到款统计页面
     router.push('/financialStatistics/commodityPayment')
   }
 
-  const resizeChart=()=>{
-    barChart.resize();
-  }
-
   const getChartData=()=>{
     getStatistic().then(res=>{
-      nextTick(()=>{
-        chartDraw(res.data.ContractMoney || [],res.data.ArrivalMoney || [],res.data.Date || [])
-      })
-    })
-  }
-
-  const chartDraw=(invoiceData,placementData,dateData)=>{
-    // console.log(barChartRef.value);
-    barChart = echarts.init(barChartRef.value)
-    barChart.setOption({
-      title: {
-        text:'开票到款统计图',
-        textStyle:{
-          fontSize:20,
-          fontWeight:500
-        },
-        left:'center'
-      },
-      legend:{
-        icon:'circle',
-        left:'center',
-        top:40,
-        textStyle:{
-          fontSize:14
-        },
-        itemGap:34,
-        itemWidth:12
-      },
-      tooltip:{
-        textStyle:{
-          align:'left'
-        }
-      },
-      grid:{
-        top:120
-      },
-      color: ['#FF903E', '#FBE947'],
-      xAxis:{
-        type: 'category',
-        data:dateData,
-        axisLabel: {
-          color: '#999', //更改坐标轴文字颜色
-          fontSize: 14, //更改坐标轴文字大小
-        },
-      },
-      yAxis: {
-        name:'万元',
-        type: 'value',
-        nameGap:20,
-        splitNumber:10,
-        nameTextStyle:{
-          align:'right',
-          color:'#999'
-        },
-        axisLabel:{
-          color:'#999',
-          formatter:(value)=>{
-            return Math.round(value*10)/100000
-          }
-        }
-      },
-      series:[{
-        data: invoiceData,
+      let invoiceData=res.data.Date.map((item,index)=> [item,res.data.ContractMoney[index]])
+      let paymentData=res.data.Date.map((item,index)=> [item,res.data.ArrivalMoney[index]])
+      basicOptions.title.text = '开票到款统计图'
+      basicOptions.series=[
+        {data: invoiceData,
         type: 'bar',
-        name:'开票金额'
-      },
-      {
-        data: placementData,
+        name:'开票金额'},
+        {data: paymentData,
         type: 'bar',
-        name:'到款金额'
-      }]
+        name:'到款金额'}
+      ]
+      chartOptions.value=basicOptions
     })
-    window.addEventListener('resize', resizeChart);
   }
 
-  onUnmounted(()=>{
-    barChart?.dispose()
-    window.removeEventListener('resize', resizeChart);
-  })
-
-  onMounted(()=>{
-    getChartData()
-  })
+  getChartData()
 
 
 </script>
@@ -145,7 +46,10 @@
           </el-icon>
         </span>
       </div>
-      <div ref="barChartRef" class="bar-chart"></div>
+
+      <div class="bar-chart">
+        <chart :options="chartOptions"/>
+      </div>
     </div>
 </template>
   

+ 2 - 2
src/views/financialManagement/invoice/invoiceList.vue

@@ -1,5 +1,5 @@
 <script setup>
-import {getSellerList} from '@/api/crm'
+import {getSellerTeamList} from '@/api/crm'
 import getCom from '../composition/IandPList'
 import '../style/iandPList.scss'
 
@@ -9,7 +9,7 @@ const data = invoice.data
 const sellerArray=ref([])
 
 const getSellerListFun=()=>{
-  getSellerList().then(res=>{
+  getSellerTeamList().then(res=>{
     sellerArray.value = res.data?.all_list || []
     // console.log(res);
   })

+ 2 - 2
src/views/financialManagement/placement/placementList.vue

@@ -1,5 +1,5 @@
 <script setup>
-import {getSellerList} from '@/api/crm'
+import {getSellerTeamList} from '@/api/crm'
 import getCom from '../composition/IandPList'
 import '../style/iandPList.scss'
 
@@ -11,7 +11,7 @@ const sellerArray=ref([])
 // ---------------method
 
 const getSellerListFun=()=>{
-  getSellerList().then(res=>{
+  getSellerTeamList().then(res=>{
     sellerArray.value = res.data?.all_list || []
     // console.log(res);
   })

+ 0 - 383
src/views/financialManagement/placementPre.vue

@@ -1,383 +0,0 @@
-<!-- 1、到款预登记,之后的版本改为预登记 - registrationPre.vue
-      2、此文件仅作备份,保留至2023-4-7 -->
-<script setup>
-import { Search } from '@element-plus/icons-vue'
-import {useRouter} from 'vue-router'
-import {getSellerList} from '@/api/crm'
-import {getCurrencyList} from '@/api/common'
-import {getPrePlacementList,prePlacementAdd,prePlacementEdit,prePlacementDelete} from '@/api/financialMana'
-
-
-const router = useRouter()
-
-  const placemenetPre=reactive({
-    searchParams:{
-      keyword:'',
-      start_date:'',
-      end_date:'',
-      sort_type:'',
-      page_size:10,
-      current:1,
-    },
-    // 创建时间数组
-    createtime:[],
-    tableData:[],
-    total:0,
-  })
-  // 销售列表
-  const sellerList = ref([])
-  // 货币列表
-  const currencyList=ref([])
-
-  // -----------------弹窗
-  const editPreForm=ref(null)
-
-  const dialog=reactive({
-    // 更改合同状态
-    editPreShow:false,
-    title:'',
-    editPreForm:{
-      company_name:'',
-      payment_date:'',
-      amount:'',
-      currency_unit:'CNY',
-      start_date:'',
-      end_date:'',
-      remark:''
-    },
-    rules:{
-      company_name:{required:true,message:'客户名称不能为空',trigger:'blur'},
-      payment_date:{required:true,message:'到款日不能为空',trigger:'blur'},
-      amount:[{required:true,message:'到款金额不能为空',trigger:'blur'},
-      {
-        validator:(rule,value,callback)=>{
-          if(!parseFloat(value)){
-            callback(new Error('到款金额格式错误'))
-          }else{
-            callback()
-          }
-        },
-        trigger:'blur'
-      }],
-      start_date:{required:true,message:'约定有效期不能为空',trigger:'change'}
-    },
-    validityDate:[]
-  })
-
-  // 监听
-  watch(()=>placemenetPre.createtime,(newVal)=>{
-    if(!newVal){
-      placemenetPre.searchParams.start_date=''
-      placemenetPre.searchParams.end_date=''
-    }else{
-      placemenetPre.searchParams.start_date = newVal[0]
-      placemenetPre.searchParams.end_date = newVal[1]
-    }
-    searchPlacementPre()
-  })
-
-  watch(()=>dialog.validityDate,(newVal)=>{
-    if(!newVal){
-      dialog.editPreForm.start_date=''
-      dialog.editPreForm.end_date=''
-    }else{
-      dialog.editPreForm.start_date = newVal[0]
-      dialog.editPreForm.end_date = newVal[1]
-    }
-  })
-
-//  --------------------------method
-
-  //获取销售列表
-  const getSellerListFun=()=>{
-    getSellerList().then(res=>{
-      sellerList.value=res.data || []
-    })
-  }
-
-  // 获取货币列表
-  const getCurrencyListFun=()=>{
-    getCurrencyList().then(res=>{
-      currencyList.value=res.data || []
-    })
-  }
-
-  // 预到款登记列表
-  const placementPreList=()=>{
-    getPrePlacementList(placemenetPre.searchParams).then(res=>{
-      placemenetPre.tableData=res.data.list || []
-      placemenetPre.total=res.data.page?.total || 0
-      // console.log(res);
-    })
-  }
-  // 切换每页的数量
-  const changePageSize=(pageSize)=>{
-    placemenetPre.searchParams.page_size = pageSize
-    placementPreList()
-  }
-  const changePageNo = (pageNo)=>{
-    placemenetPre.searchParams.current = pageNo
-    placementPreList()
-  }
-  const searchPlacementPre=()=>{
-    placemenetPre.searchParams.current = 1
-    placementPreList()
-  }
-
-  const sortChange=({order})=>{
-    console.log(order);
-    placemenetPre.searchParams.sort_type = order=='ascending'?1:order=='descending'?2:''
-    searchPlacementPre()
-  }
-
-  // 补录合同
-  const supplementaryContract=(row)=>{  
-    // console.log(id);
-    router.push({path:'/financial/list/contractProgress',query:{
-      supplementaryId:row.pre_pay_id,
-      company_name:row.company_name,
-      currency_unit:row.currency_unit,
-      placement_amount:row.origin_amount,
-      start_date:row.start_date,
-      end_date:row.end_date
-    }})
-  }
-
-  //新增预到款
-  const addPre=()=>{
-    dialog.title='新增到款预登记'
-    dialog.editPreShow=true
-  }
-  //编辑预到款
-  const editPre=(row)=>{
-    dialog.editPreForm.pre_pay_id = row.pre_pay_id
-    dialog.editPreForm.company_name=row.company_name
-    dialog.editPreForm.payment_date=row.payment_date
-    dialog.editPreForm.amount=row.origin_amount
-    dialog.editPreForm.currency_unit=row.currency_unit
-    dialog.editPreForm.remark=row.remark
-    dialog.validityDate=[row.start_date,row.end_date]
-    dialog.title='编辑到款预登记'
-    dialog.editPreShow=true
-  }
-
-  //弹窗关闭动画 回调
-  const dialogClosed=()=>{
-    dialog.editPreForm.pre_pay_id = ''
-    dialog.editPreForm.company_name=''
-    dialog.editPreForm.payment_date=''
-    dialog.editPreForm.amount=''
-    dialog.editPreForm.currency_unit='CNY'
-    dialog.editPreForm.remark=''
-    dialog.validityDate=[]
-    setTimeout(()=>{
-      editPreForm.value.clearValidate()
-    },0)
-  }
-
-  // 提交
-  const submitForm=()=>{
-    editPreForm.value.validate((valid)=>{
-      if(valid){
-        console.log(dialog.editPreForm);
-        dialog.editPreForm.amount = parseFloat(dialog.editPreForm.amount)
-        let prePlacementProp=prePlacementAdd
-        if(dialog.editPreForm.pre_pay_id){
-          // 编辑
-          prePlacementProp=prePlacementEdit
-        }
-        prePlacementProp(dialog.editPreForm).then(res=>{
-          dialog.editPreShow=false
-          ElMessage.success(`${dialog.title}成功`)
-          placementPreList()
-        })
-      }
-    })
-  }
-  // 删除
-  const delteRecord=(row)=>{
-    let hintText = '已补录合同,删除后不可恢复,是否确认删除该条到款预登记信息?'
-    if(row.contract_register_id== 0){
-      hintText='未补录合同,删除后不可恢复,是否确认删除该条到款预登记信息?'
-    }
-    ElMessageBox.confirm(hintText,'操作提示',    
-    {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-      type: 'warning',
-    }).then(res=>{
-      prePlacementDelete({pre_pay_id:row.pre_pay_id}).then(res=>{
-        ElMessage.success('删除成功')
-        placementPreList()
-      })
-    }).catch(()=>{})
-  }
-
-  //  ---------------------created
-  // getSellerListFun()
-  getCurrencyListFun()
-  placementPreList()
-</script>
-
-<template>
-  <div class="placemenetPre-list-container" id="placemenetPre-list-container" > 
-    <!-- 顶部区域 -->
-    <div class="placemenetPre-top-zone">
-      <div class="placemenetPre-top-search-zone">
-        <el-input v-model="placemenetPre.searchParams.keyword" placeholder="客户姓名" :prefix-icon="Search"
-        style="width: 340px;margin-right: 30px;" @input="searchPlacementPre" clearable />
-        <!-- <el-date-picker v-model="placemenetPre.createtime" start-placeholder="开始日期"
-        end-placeholder="结束日期" style="max-width: 240px;"
-        value-format="YYYY-MM-DD" type="daterange"></el-date-picker> -->
-      </div>
-      <!-- 按钮区域 -->
-      <div class="placemenetPre-top-option-zone" v-permission="'financial:placementPre:add'">
-        <el-button type="primary" size="large" style="color: white;"
-        @click="addPre" class="element-common-button">新增</el-button>         
-      </div>
-    </div>
-    <div class="placemenetPre-table-zone">
-      <!-- 表格 -->
-      <el-table :data="placemenetPre.tableData" border max-height="695px"
-      size="default" style="position: sticky;"> 
-        <el-table-column label="客户名称" prop="company_name"
-        show-overflow-tooltip min-width="120"></el-table-column>
-        <el-table-column label="到款日" prop="payment_date"
-        show-overflow-tooltip min-width="110"></el-table-column>
-        <el-table-column label="到款金额" prop="origin_amount" width="120"></el-table-column>
-        <el-table-column label="金额单位" prop="unit_name" width="90">
-        </el-table-column>
-        <el-table-column label="约定有效期" width="210" prop="start_date">
-          <template #default="{row}">
-            {{(row.start_date+' 至 '+row.end_date)}}
-          </template>
-        </el-table-column>
-        <el-table-column label="备注" prop="remark" show-overflow-tooltip></el-table-column>
-        <!-- <el-table-column label="创建时间" prop="create_time" width="165" sortable="custom"></el-table-column> -->
-        <el-table-column label="创建人" width="90" prop="admin_name"></el-table-column>
-        <el-table-column label="操作" fixed="right" 
-        v-permission="['financial:placementPre:supplementary','financial:placementPre:edit','financial:placementPre:delete']">
-          <template #default="{row}">
-            <div class="table-options">
-              <span class="table-option-buttons" v-permission="'financial:placementPre:supplementary'"
-              @click="supplementaryContract(row)">
-                补录合同
-              </span>
-              <span class="table-option-buttons" v-permission="'financial:placementPre:edit'"
-              @click="editPre(row)">
-                编辑
-              </span>
-              <span class="table-option-buttons" v-permission="'financial:placementPre:delete'"
-              @click="delteRecord(row)" style="color:var(--dangerColor);">
-                删除
-              </span>
-            </div>
-          </template>
-        </el-table-column>
-        <template #empty>
-          <div class="table-no-data">
-            <img src="@/assets/img/icon/empty-data.png" />
-            <span>暂无数据</span>
-          </div>
-        </template>
-      </el-table>
-      <!-- 分页 -->
-      <m-page :pageSize="placemenetPre.searchParams.page_size" :page_no="placemenetPre.searchParams.current" 
-      style="display: flex;justify-content: flex-end;margin-top: 20px;" 
-      :total="placemenetPre.total" @handleCurrentChange="changePageNo" @handleSizeChange="changePageSize"/>
-    </div>
-    <!-- 新增/编辑弹窗 -->
-    <el-dialog v-model="dialog.editPreShow" :title="dialog.title" width="556px" @closed="dialogClosed"
-     :close-on-click-modal="false">
-      <!-- <template style="display: flex;justify-content: center;"> -->
-        <el-form :model="dialog.editPreForm" ref="editPreForm" label-width="130px"
-        :rules="dialog.rules" style="margin-top: 10px;">
-          <el-form-item label="客户名称" prop="company_name">
-            <el-input v-model="dialog.editPreForm.company_name"
-            placeholder="请输入客户名称" style="width:346px" />
-          </el-form-item>
-          <el-form-item label="到款日" prop="payment_date">
-            <el-date-picker v-model="dialog.editPreForm.payment_date" placeholder="请选择到款日" 
-            :clearable="false" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
-            ></el-date-picker>
-          </el-form-item>
-          <el-form-item label="到款金额" id="contractInfo-contractAmount"
-          prop="amount">
-            <el-input v-model.trim="dialog.editPreForm.amount"
-            placeholder="请输入到款金额" style="width: 206px;" />
-            <el-select v-model="dialog.editPreForm.currency_unit" placeholder="请选择货币类型" 
-            style="margin-left: 20px;width: 120px;">
-              <el-option v-for="item in currencyList" :key="item.code" :label="item.name" :value="item.code">
-              </el-option>
-            </el-select>
-          </el-form-item>
-          <el-form-item label="约定有效期" prop="start_date">
-            <el-date-picker type="daterange" 
-            v-model="dialog.validityDate" style="max-width: 346px;"
-            start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD"
-            :clearable="false">
-            </el-date-picker>
-          </el-form-item>
-          <el-form-item label="备注" prop="remark">
-            <el-input
-              v-model="dialog.editPreForm.remark"
-              style="width: 346px;"
-              :rows="3"
-              placeholder="请输入内容"
-              type="textarea"
-            />
-          </el-form-item>
-        </el-form>
-      <!-- </template> -->
-      <template #footer>
-        <div>
-          <el-button @click="dialog.editPreShow=false">取消</el-button>
-          <el-button type="primary" @click="submitForm" style="margin-left: 30px;">确定</el-button>
-        </div>
-      </template> 
-    </el-dialog>
-  </div>
-</template>
-  
-<style lang="scss" scoped>
-  .placemenetPre-list-container{
-    min-height: 100%;
-    .placemenetPre-top-zone{
-      display: flex;
-      flex-wrap: wrap;
-      align-items: flex-start;
-      justify-content: space-between;
-      margin-bottom: 20px;
-      .placemenetPre-top-search-zone{
-        display: flex;
-        align-items: center;
-        margin-bottom: 8px;
-      }
-      .placemenetPre-top-option-zone{
-        // margin-bottom: 8px;
-        .element-common-button{
-          width: 118px;
-          margin-left: 30px;
-          font-size: 14px;
-        }
-      }
-    }
-    .placemenetPre-table-zone{
-      margin-top: 20px;
-    }
-  }
-</style>
-<style lang="scss">
-  #placemenetPre-list-container{
-    .el-dialog__footer{
-      padding-top: 0!important;
-      padding: 0 0 36px 0;
-    }
-    .el-date-editor{
-      .el-input__wrapper{
-        width: 346px;
-      }
-    }
-  }
-
-</style>

+ 212 - 0
src/views/financialStatistics/businessIncome.vue

@@ -0,0 +1,212 @@
+<script setup>
+  import {downloadByFlow} from '@/utils/common-methods'
+  import {getSellerTeamList} from '@/api/crm'
+  import {getIncomeChartData} from '@/api/financialStatistics'
+  import chart from '@/components/echart/index.vue'
+  import {baseOptions} from '@/components/echart/baseOptions'
+
+  const basicOptions=baseOptions()
+
+  const dimensionArray=[{label:'月度',tag:0},{label:'季度',tag:1},{label:'半年度',tag:2},{label:'年度',tag:3},{label:'月度累计',tag:4}]
+  const customerTypeArray=[{label:'全部',value:0},{label:'新客户',value:1},{label:'老客户',value:2}]
+  const sellerArray=ref(null)
+
+  const searchParams=reactive({
+    list_param:0,
+    company_type:0,
+    start_date:'',
+    end_date:'',
+    seller_ids:'',
+    is_export:0,
+    // 不是后端所需要的值,在axios中用到的值,具体请看@/utils/request.js文件
+    takeLastRequestResult:true
+  })
+  // 加载状态
+  const loading=reactive({
+    list:true,
+    export:false
+  })
+  const chartOptions=ref(basicOptions)
+  
+  const invoiceTime=ref(null)
+  watch(invoiceTime,(value)=>{
+    if(!value){
+      searchParams.start_date=searchParams.end_date = ''
+    }else{
+      searchParams.start_date = value[0]
+      searchParams.end_date=value[1]
+    }
+    getList()
+  })
+
+  const sellersCheckList=ref(null)
+  watch(sellersCheckList,(value)=>{
+    if(!value){
+      searchParams.seller_ids = ''
+    }else{
+      searchParams.seller_ids = value.join(',')
+    }
+    getList()
+  })
+
+  const getSellerData=()=>{
+    getSellerTeamList().then(res=>{
+      sellerArray.value = res.data?.all_list || []
+    })
+  }
+
+  const initOptions=()=>{
+    chartOptions.value.title.text = "业务收入金额统计图"
+    chartOptions.value.title.textStyle.color='#333333'
+    chartOptions.value.color=['#FF903E', '#409EFF']
+    // x轴配置
+    chartOptions.value.xAxis.axisLabel.align='center' 
+    chartOptions.value.xAxis.axisLabel.rotate=45
+    chartOptions.value.xAxis.axisLabel.margin=27
+    // Y轴配置
+    chartOptions.value.yAxis[0].splitLine={
+      lineStyle:{
+        type:'dashed'
+      }
+    }
+    chartOptions.value.yAxis[0].splitNumber=8
+    chartOptions.value.yAxis[1]={
+      alignTicks:true,
+      axisLabel:{
+        color:'#999',
+        formatter:(value)=>{
+          // 防止失真
+          return Math.round(value*10000)/10000
+        }
+      },
+      splitLine:{
+        lineStyle:{
+          type:'dashed'
+        }
+      },
+      splitNumber:8
+    }
+      
+    // 数据配置
+    chartOptions.value.series=[
+      {
+        data: [],
+        type: 'bar',
+        name:'业务收入金额'
+      },
+      {
+        data: [],
+        type: 'line',
+        lineStyle:{
+          width:3
+        },
+        yAxisIndex:1,
+        name:'同比值',
+        connectNulls:true,
+        symbol:'circle',
+        symbolSize:2
+      }
+    ]
+  }
+  const getList=()=>{
+    loading.list=true
+    getIncomeChartData(searchParams).then(res=>{
+      // console.log(res);
+      let dates = res.data.Date || []
+      chartOptions.value.series[0].data=dates.map((item,index)=> [item,res.data.TotalMoney[index]])
+      chartOptions.value.series[1].data=dates.map((item,index)=> [item,res.data.Yoy[index]])
+      loading.list=false
+    }).catch(err=>{
+      if(!err.__CANCEL__){
+        // 不是因为取消的错误才设置为false
+        loading.list=false
+      }
+    })
+  }
+
+  const exportData=()=>{
+    loading.export=true
+    getIncomeChartData({...searchParams,is_export:1}).then(res=>{
+      downloadByFlow(res,'xlxs','业务收入金额统计')
+    }).finally(()=>{
+      loading.export=false
+    })
+  }
+
+  initOptions()
+  getSellerData()
+  getList()
+</script>
+
+<template>
+    <div class="business-income-container" id="business-income-container">
+      <div class="business-income-top">
+        <div class="business-income-search-zone">
+          <el-radio-group v-model="searchParams.list_param" size="large" @change="getList"
+          style="margin: 0 30px 8px 0;">
+            <el-radio-button v-for="item in dimensionArray" :key="item.tag" class="dimension-radio"
+            :label="item.tag" >{{ item.label }}</el-radio-button>
+          </el-radio-group>
+          <el-radio-group v-model="searchParams.company_type" size="large" @change="getList"
+          style="margin: 0 30px 8px 0;">
+            <el-radio-button v-for="item in customerTypeArray" :key="item.value" class="dimension-radio"
+            :label="item.value" >{{ item.label }}</el-radio-button>
+          </el-radio-group>
+          <el-date-picker v-model="invoiceTime" type="monthrange"
+          start-placeholder="开始日期" end-placeholder="结束日期" style="max-width: 238px;margin:0 30px 8px 0;"
+          value-format="YYYY-MM"></el-date-picker>
+          <el-cascader :options="sellerArray" v-model="sellersCheckList" clearable placeholder="请选择销售"
+          collapse-tags :show-all-levels="false" collapse-tags-tooltip key="seller" filterable 
+          style="max-width: 238px;margin:0 30px 8px 0;"
+          :props="{multiple:true,label:'seller_name',value:'seller_id',children:'child',emitPath:false}" ></el-cascader>
+        </div>
+        <el-button @click="exportData" type="primary" size="large" style="width: 118px;" :loading="loading.export">导出数据</el-button>
+      </div>
+      <div class="business-income-chart" v-loading="loading.list" element-loading-text="图表加载中……">
+        <chart :options="chartOptions" style="width: 75%;height: 80%;"/>
+      </div>
+    </div>
+</template>
+  
+<style lang="scss" scoped>
+  .business-income-container{
+    min-height: calc(100vh - 120px);
+    box-sizing: border-box;
+    .business-income-top{
+      display: flex;
+      justify-content: space-between;
+      margin-bottom: 20px;
+      .business-income-search-zone{
+        display: flex;
+        flex-wrap: wrap;
+      }
+    }
+    .business-income-chart{
+      border: 1px solid #DCDFE6;
+      background-color: white;
+      height: calc(100vh - 190px);
+      padding: 20px 30px;
+      box-sizing: border-box;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+    }
+
+  }
+</style>
+<style lang="scss">
+  #business-income-container{
+    .dimension-radio{
+      span{
+        display: inline-block;
+        color: $hitTextColorTwo;
+        padding: 12px 12px;
+      }
+    }
+    .is-active{
+      span{
+        color:white
+      }
+    }
+  }
+</style>

+ 2 - 2
src/views/financialStatistics/commodityPayment.vue

@@ -2,7 +2,7 @@
 import { Search } from '@element-plus/icons-vue'
 // import dropdownText from '@/components/dropdown-text/index.vue'  
 import {getInvoicePaymentList,updatePayType,setServiceAmount} from '@/api/financialStatistics'
-import {getSellerGroupList,getSellerList} from '@/api/crm'
+import {getSellerGroupList,getSellerTeamList} from '@/api/crm'
 import {getSimpleServiceList} from '@/api/financialMana'
 import {downloadByFlow} from '@/utils/common-methods'
 import elementResizeDetectorMaker from 'element-resize-detector';
@@ -176,7 +176,7 @@ const moment = inject('$moment')
   }
   // 获取销售列表
   const getSellerListFun=()=>{
-    getSellerList().then(res=>{
+    getSellerTeamList().then(res=>{
       sellerArray.value = res.data?.all_list || []
       // console.log(res);
     })