cz 1 year ago
parent
commit
34dd745f1c

+ 10 - 0
src/components/PDF/fundsPDF.vue

@@ -547,6 +547,16 @@
             <div
               style="width: 126px; border-right: 1px solid black; padding: 8px"
             >
+              付款账户
+            </div>
+            <div style="width: calc(100% - 126px); padding: 8px">
+              {{ printDetails.accountManagementName }}
+            </div>
+          </div>
+          <div style="display: flex; border-bottom: 1px solid black">
+            <div
+              style="width: 126px; border-right: 1px solid black; padding: 8px"
+            >
               电子发票(PDF/JPG)
             </div>
             <div style="width: calc(100% - 126px); padding: 8px">

+ 1 - 1
src/views/EHSD/procurement/purchasedEHSD/index.vue

@@ -225,7 +225,7 @@ const config = computed(() => {
       },
       renderHTML(row) {
         return [
-          row.status == 30 && Number(row.amount) > Number(row.sumPayMoney)
+          row.status == 30
             ? {
                 attrs: {
                   label: "变更",

+ 6 - 4
src/views/dataBoard/board/customerAnalysis/index.vue

@@ -1,11 +1,13 @@
 <template>
   <div class="box">
-    <div class="query bck">
+    <div class="query bck" style="margin-top: 10px">
       <el-form :inline="true" :model="queryForm">
         <el-form-item label="国家">
           <el-select
             v-model="queryForm.countryId"
             placeholder="请选择"
+            clearable
+            filterable
             @change="onQuery"
           >
             <el-option
@@ -166,7 +168,6 @@
 
 <script setup>
 import * as echarts from "echarts";
-
 import TitleInfo from "@/components/TitleInfo/index.vue";
 const titleList = ["客户来源统计", "类型统计", "趋势分析"];
 const { proxy } = getCurrentInstance();
@@ -352,10 +353,11 @@ const onQuery = () => {
 const getCountryData = () => {
   proxy.post("/customizeArea/list", { parentId: "0" }).then((res) => {
     countryData.value = res;
-    queryForm.countryId = "44";
+    queryForm.countryId = "";
     let endData = new Date();
     let beginDate = new Date();
-    beginDate.setFullYear(endData.getFullYear() - 1);
+    beginDate.setMonth(0);
+    beginDate.setDate(1);
     queryForm.timeArr = [
       proxy.parseTime(beginDate, "{y}-{m}-{d}"),
       proxy.parseTime(endData, "{y}-{m}-{d}"),

+ 640 - 0
src/views/dataBoard/board/employeeAnalysis/index.vue

@@ -0,0 +1,640 @@
+<template>
+  <div class="tenant">
+    <div class="query bck" style="padding-bottom: 0px">
+      <el-form :inline="true" :model="queryForm">
+        <el-form-item label="查看用户">
+          <el-select
+            v-model="selectUser"
+            multiple
+            placeholder="请选择用户"
+            @change="changeUser"
+          >
+            <el-option
+              v-for="item in userList"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+              :disabled="item.value !== 'all' && selectUser.includes('all')"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="日期">
+          <el-date-picker
+            v-model="queryForm.timeArr"
+            type="daterange"
+            unlink-panels
+            range-separator="-"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            value-format="YYYY-MM-DD"
+            @change="onQuery"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button @click="clickBtn(1)" :class="{ btnBck: selectBtn === 1 }"
+            >近30天</el-button
+          >
+          <el-button @click="clickBtn(2)" :class="{ btnBck: selectBtn === 2 }"
+            >本月</el-button
+          >
+          <el-button @click="clickBtn(3)" :class="{ btnBck: selectBtn === 3 }"
+            >上月</el-button
+          >
+          <el-button @click="clickBtn(4)" :class="{ btnBck: selectBtn === 4 }"
+            >近一年</el-button
+          >
+          <el-button @click="clickBtn(5)" :class="{ btnBck: selectBtn === 5 }"
+            >去年</el-button
+          >
+          <el-button @click="clickBtn(6)" :class="{ btnBck: selectBtn === 6 }"
+            >今年</el-button
+          >
+        </el-form-item>
+      </el-form>
+    </div>
+    <div v-loading="loading">
+      <div style="display: flex; margin-top: 15px">
+        <div class="bck">
+          <TitleInfo :content="titleList[0]"></TitleInfo>
+          <div class="one-row" style="margin-top: 15px">
+            <div class="one-row-item first" style="margin-right: 15px">
+              <div class="label">存量客户(人)</div>
+              <div class="value">
+                {{ allData.customerSituation.customerCount }}
+              </div>
+            </div>
+            <div class="one-row-item other" style="margin-right: 15px">
+              <div class="label">新增客户(人)</div>
+              <div class="value">
+                {{ allData.customerSituation.customerAddCount }}
+              </div>
+            </div>
+            <div class="one-row-item other" style="margin-right: 15px">
+              <div class="label">报价客户(人)</div>
+              <div class="value">
+                {{ allData.customerSituation.saleQuotationCustomerCount }}
+              </div>
+            </div>
+            <div class="one-row-item other">
+              <div class="label">成交客户(人)</div>
+              <div class="value">
+                {{ allData.customerSituation.contractCustomerCount }}
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="bck" style="margin-left: 20px">
+          <TitleInfo :content="titleList[1]"></TitleInfo>
+          <div class="one-row" style="margin-top: 15px">
+            <div class="one-row-item first" style="margin-right: 15px">
+              <div class="label">存量产品</div>
+              <div class="value">
+                {{ allData.productSituation.productCount }}
+              </div>
+            </div>
+            <div class="one-row-item other">
+              <div class="label">新增产品</div>
+              <div class="value">
+                {{ allData.productSituation.productAddCount }}
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="bck" style="margin-left: 20px; flex: 1">
+          <TitleInfo :content="titleList[2]"></TitleInfo>
+          <div class="one-row" style="margin-top: 15px">
+            <div class="one-row-item first" style="margin-right: 15px">
+              <div class="label">发件</div>
+              <div class="value">{{ allData.commMail.sentMailCount }}</div>
+            </div>
+            <div class="one-row-item other">
+              <div class="label">收件</div>
+              <div class="value">{{ allData.commMail.receiveMailCount }}</div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div style="margin-top: 15px">
+        <div class="bck scroll">
+          <TitleInfo :content="titleList[3]"></TitleInfo>
+          <div class="two-row" style="margin-top: 15px">
+            <div class="two-row-item first" style="margin-right: 15px">
+              <div class="label">报价</div>
+              <div class="value">
+                <div style="margin-right: 15px">
+                  <div class="child-value">
+                    {{ allData.saleSituation.quotationAddStatistics.count }}
+                  </div>
+                  <div class="text">订单数</div>
+                </div>
+                <div>
+                  <div class="child-value color-blue">
+                    CNY
+                    {{
+                      moneyFormat(
+                        allData.saleSituation.quotationAddStatistics.sumAmount,
+                        2
+                      )
+                    }}
+                  </div>
+                  <div class="text">金额</div>
+                </div>
+              </div>
+            </div>
+            <div class="two-row-item other" style="margin-right: 15px">
+              <div class="label">成交</div>
+              <div class="value">
+                <div style="margin-right: 15px">
+                  <div class="child-value">
+                    {{ allData.saleSituation.contractAddStatistics.count }}
+                  </div>
+                  <div class="text">订单数</div>
+                </div>
+                <div>
+                  <div class="child-value color-blue">
+                    CNY
+                    {{
+                      moneyFormat(
+                        allData.saleSituation.contractAddStatistics.sumAmount,
+                        2
+                      )
+                    }}
+                  </div>
+                  <div class="text">金额</div>
+                </div>
+              </div>
+            </div>
+            <div class="two-row-item other" style="margin-right: 15px">
+              <div class="label">到账</div>
+              <div class="value">
+                <div style="margin-right: 15px">
+                  <div class="child-value">
+                    {{ allData.saleSituation.claimAddStatistics.count }}
+                  </div>
+                  <div class="text">笔</div>
+                </div>
+                <div>
+                  <div class="child-value color-blue">
+                    CNY
+                    {{
+                      moneyFormat(
+                        allData.saleSituation.claimAddStatistics.sumAmount,
+                        2
+                      )
+                    }}
+                  </div>
+                  <div class="text">金额</div>
+                </div>
+              </div>
+            </div>
+            <div class="two-row-item other">
+              <div class="label">创建单证</div>
+              <div class="value">
+                <div style="margin-right: 15px">
+                  <div class="child-value">
+                    {{ allData.saleSituation.documentsCount }}
+                  </div>
+                  <div class="text">套</div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div style="margin-top: 15px">
+        <div class="bck scroll">
+          <TitleInfo :content="titleList[4]"></TitleInfo>
+          <div class="two-row" style="margin-top: 15px">
+            <div class="two-row-item first" style="margin-right: 15px">
+              <div class="label">报价</div>
+              <div class="value">
+                <div style="margin-right: 15px">
+                  <div class="child-value">
+                    {{ allData.saleAddSituation.quotationAddStatistics.count }}
+                  </div>
+                  <div class="text">订单数</div>
+                </div>
+                <div>
+                  <div class="child-value color-blue">
+                    CNY
+                    {{
+                      moneyFormat(
+                        allData.saleAddSituation.quotationAddStatistics
+                          .sumAmount,
+                        2
+                      )
+                    }}
+                  </div>
+                  <div class="text">金额</div>
+                </div>
+              </div>
+            </div>
+            <div class="two-row-item other" style="margin-right: 15px">
+              <div class="label">成交</div>
+              <div class="value">
+                <div style="margin-right: 15px">
+                  <div class="child-value">
+                    {{ allData.saleAddSituation.contractAddStatistics.count }}
+                  </div>
+                  <div class="text">订单数</div>
+                </div>
+                <div>
+                  <div class="child-value color-blue">
+                    CNY
+                    {{
+                      moneyFormat(
+                        allData.saleAddSituation.contractAddStatistics
+                          .sumAmount,
+                        2
+                      )
+                    }}
+                  </div>
+                  <div class="text">金额</div>
+                </div>
+              </div>
+            </div>
+            <div class="two-row-item other" style="margin-right: 15px">
+              <div class="label">到账</div>
+              <div class="value">
+                <div style="margin-right: 15px">
+                  <div class="child-value">
+                    {{ allData.saleAddSituation.claimAddStatistics.count }}
+                  </div>
+                  <div class="text">笔</div>
+                </div>
+                <div>
+                  <div class="child-value color-blue">
+                    CNY
+                    {{
+                      moneyFormat(
+                        allData.saleAddSituation.claimAddStatistics.sumAmount,
+                        2
+                      )
+                    }}
+                  </div>
+                  <div class="text">金额</div>
+                </div>
+              </div>
+            </div>
+            <div class="two-row-item other">
+              <div class="label">创建单证</div>
+              <div class="value">
+                <div style="margin-right: 15px">
+                  <div class="child-value">
+                    {{ allData.saleAddSituation.documentsCount }}
+                  </div>
+                  <div class="text">套</div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div style="display: flex; margin-top: 15px">
+        <div class="bck" style="width: 100%">
+          <TitleInfo :content="titleList[5]"></TitleInfo>
+          <div class="two-row" style="margin-top: 15px; width: 100%">
+            <div class="two-row-item first" style="margin-right: 15px">
+              <div class="label">待采购</div>
+              <div class="value">
+                <div style="margin-right: 15px">
+                  <div class="child-value">
+                    {{
+                      allData.purchaseStatistics.waitPurchaseStatistics
+                        .contractProductCount
+                    }}
+                  </div>
+                  <div class="text">交接单</div>
+                </div>
+                <div>
+                  <div class="child-value color-blue">
+                    {{
+                      allData.purchaseStatistics.waitPurchaseStatistics
+                        .subscribeDetailCount
+                    }}
+                  </div>
+                  <div class="text">申购</div>
+                </div>
+              </div>
+            </div>
+            <div class="two-row-item other" style="margin-right: 15px">
+              <div class="label">采购订单</div>
+              <div class="value">
+                <div style="margin-right: 15px">
+                  <div class="child-value">
+                    {{ allData.purchaseStatistics.purchaseOrder.purchaseCount }}
+                  </div>
+                  <div class="text">订单数</div>
+                </div>
+                <div style="margin-right: 15px">
+                  <div class="child-value color-blue">
+                    CNY
+                    {{
+                      moneyFormat(
+                        allData.purchaseStatistics.purchaseOrder.purchaseAmount,
+                        2
+                      )
+                    }}
+                  </div>
+                  <div class="text">金额</div>
+                </div>
+                <div>
+                  <div class="child-value color-blue">
+                    CNY
+                    {{
+                      moneyFormat(
+                        allData.purchaseStatistics.purchaseOrder
+                          .purchasePayAmount,
+                        2
+                      )
+                    }}
+                  </div>
+                  <div class="text">已付款</div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- <div class="bck" style="margin-left: 20px">
+          <TitleInfo :content="titleList[6]"></TitleInfo>
+          <div class="one-row" style="margin-top: 15px">
+            <div class="one-row-item first" style="margin-right: 15px">
+              <div class="label">发起</div>
+              <div class="value">
+                {{ allData.flowStatistics.flowExampleCount }}
+              </div>
+            </div>
+            <div class="one-row-item other">
+              <div class="label">待处理</div>
+              <div class="value">
+                {{ allData.flowStatistics.waitFlowExampleCount }}
+              </div>
+            </div>
+          </div>
+        </div> -->
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import TitleInfo from "@/components/TitleInfo/index.vue";
+import useUserStore from "@/store/modules/user";
+import moment from "moment";
+
+const titleList = [
+  "客户情况",
+  "产品情况",
+  "往来邮件",
+  "销售行为(存量客户)",
+  "销售行为(新增客户)",
+  "采购行为",
+  "流程统计",
+];
+const userList = ref([]);
+const selectUser = ref(["all"]);
+const { proxy } = getCurrentInstance();
+const loading = ref(false);
+const queryForm = reactive({
+  userIds: [],
+  beginTime: "",
+  endTime: "",
+  timeArr: "",
+});
+const allData = ref({
+  customerSituation: {},
+  productSituation: {},
+  commMail: {},
+  flowStatistics: {},
+  saleSituation: {
+    quotationAddStatistics: {},
+    contractAddStatistics: {},
+    claimAddStatistics: {},
+  },
+  saleAddSituation: {
+    quotationAddStatistics: {},
+    contractAddStatistics: {},
+    claimAddStatistics: {},
+  },
+  purchaseStatistics: {
+    waitPurchaseStatistics: {},
+    purchaseOrder: {},
+  },
+});
+const getData = () => {
+  loading.value = true;
+  proxy.post("/employeeAnalysis/info", queryForm).then((res) => {
+    allData.value = res;
+    loading.value = false;
+  });
+};
+const getDict = () => {
+  proxy
+    .get("/tenantUser/list", {
+      pageNum: 1,
+      pageSize: 10000,
+      tenantId: useUserStore().user.tenantId,
+    })
+    .then((res) => {
+      userList.value = res.rows.map((item) => {
+        return {
+          label: item.nickName,
+          value: item.userId,
+        };
+      });
+      userList.value.unshift({
+        label: "全部",
+        value: "all",
+      });
+    });
+};
+
+const onQuery = () => {
+  if (queryForm.timeArr.length > 1) {
+    queryForm.beginTime = queryForm.timeArr[0];
+    queryForm.endTime = queryForm.timeArr[1];
+  } else {
+    queryForm.beginTime = "";
+    queryForm.endTime = "";
+  }
+  getData();
+};
+
+const changeUser = (val) => {
+  selectUser.value = val.includes("all") ? ["all"] : val;
+  queryForm.userIds = val.includes("all") ? [] : val;
+  onQuery();
+};
+
+const selectBtn = ref(1);
+const clickBtn = (val) => {
+  selectBtn.value = val;
+  let today = new Date();
+  let startDate = null;
+  let endDate = null;
+  let year = null;
+  let month = null;
+  switch (val) {
+    case 1:
+      startDate = new Date(today);
+      startDate.setDate(today.getDate() - 29);
+      endDate = new Date(today);
+      break;
+    case 2:
+      year = today.getFullYear();
+      month = today.getMonth();
+      startDate = new Date(year, month, 1);
+      endDate = new Date(year, month + 1, 0);
+      break;
+    case 3:
+      year = today.getFullYear();
+      month = today.getMonth() - 1;
+      startDate = new Date(year, month, 1);
+      endDate = new Date(year, month + 1, 0);
+      break;
+    case 4:
+      year = today.getFullYear();
+      startDate = new Date(year - 1, today.getMonth(), today.getDate());
+      endDate = new Date(year, today.getMonth(), today.getDate());
+      break;
+    case 5:
+      year = today.getFullYear();
+      startDate = new Date(year - 1, 0, 1);
+      endDate = new Date(year - 1, 11, 31);
+      break;
+    case 6:
+      year = today.getFullYear();
+      startDate = new Date(year, 0, 1);
+      endDate = new Date(year, 11, 31);
+      break;
+    default:
+      startDate = new Date(today);
+      startDate.setDate(today.getDate() - 29);
+      endDate = new Date(today);
+      break;
+  }
+  queryForm.timeArr = [
+    moment(startDate).format("yyyy-MM-DD"),
+    moment(endDate).format("yyyy-MM-DD"),
+  ];
+  onQuery();
+};
+
+getDict();
+clickBtn(1);
+</script>
+
+<style lang="scss" scoped>
+.tenant {
+  padding: 20px;
+}
+.bck {
+  background-color: #fff;
+  padding: 15px;
+}
+.one-row {
+  display: flex;
+  .one-row-item {
+    min-width: 160px;
+    max-width: 180px;
+    padding: 15px;
+    border-radius: 10px;
+    .label {
+      margin-bottom: 10px;
+      font-size: 14px;
+      color: #333333;
+    }
+    .value {
+      font-size: 24px;
+      font-weight: 700;
+    }
+  }
+
+  .first {
+    background: #fff1e1;
+    .value {
+      color: #333333;
+    }
+    .label::before {
+      content: "";
+      display: inline-block;
+      width: 10px;
+      height: 10px;
+      background: #ff9315;
+      border-radius: 50%;
+      margin-right: 10px;
+    }
+  }
+
+  .other {
+    background: #eff6ff;
+    .value {
+      color: #333333;
+    }
+    .label::before {
+      content: "";
+      display: inline-block;
+      width: 10px;
+      height: 10px;
+      background: #0084ff;
+      border-radius: 50%;
+      margin-right: 10px;
+    }
+  }
+}
+.two-row {
+  display: flex;
+  .two-row-item {
+    min-width: 320px;
+    max-width: 320px;
+    padding: 15px;
+    border-radius: 10px;
+    .label {
+      margin-bottom: 10px;
+      font-size: 14px;
+      font-weight: 700;
+      color: #333333;
+    }
+    .value {
+      display: flex;
+      font-size: 14px;
+      justify-content: space-between;
+      padding-right: 30px;
+      .child-value {
+        font-size: 16px;
+        font-weight: 700;
+      }
+      .text {
+        // color: #333333;
+        font-size: 14px;
+        margin-top: 10px;
+      }
+    }
+  }
+  .first {
+    background: #fff1e1;
+    // .child-value {
+    //   color: #333333;
+    // }
+  }
+  .other {
+    background: #eff6ff;
+    // .child-value {
+    //   color: #0084ff;
+    // }
+  }
+  .color-blue {
+    color: #0084ff;
+  }
+}
+.btnBck {
+  background: #0084ff;
+  color: #fff;
+}
+.scroll {
+  overflow-x: auto;
+}
+</style>

+ 185 - 35
src/views/dataBoard/board/productAnalysis/index.vue

@@ -3,8 +3,20 @@
     <div style="background-color: white; padding: 20px">
       <el-form :inline="true" :model="queryForm">
         <el-form-item label="国家">
-          <el-select v-model="queryForm.countryId" placeholder="请选择" @change="onQuery">
-            <el-option v-for="item in countryData" :label="item.chineseName" :value="item.id" :key="item.id"> </el-option>
+          <el-select
+            v-model="queryForm.countryId"
+            placeholder="请选择"
+            clearable
+            filterable
+            @change="onQuery"
+          >
+            <el-option
+              v-for="item in countryData"
+              :label="item.name"
+              :value="item.id"
+              :key="item.id"
+            >
+            </el-option>
           </el-select>
         </el-form-item>
         <el-form-item label="日期">
@@ -16,7 +28,8 @@
             start-placeholder="开始日期"
             end-placeholder="结束日期"
             value-format="YYYY-MM-DD"
-            @change="onQuery" />
+            @change="onQuery"
+          />
         </el-form-item>
         <el-form-item>
           <el-button type="primary" @click="onQuery">搜索</el-button>
@@ -25,46 +38,122 @@
     </div>
     <el-row style="margin-top: 20px" type="flex">
       <el-col :span="15">
-        <div style="margin-right: 20px; background-color: white; padding: 20px 0 0 20px; height: 100%">
+        <div
+          style="
+            margin-right: 20px;
+            background-color: white;
+            padding: 20px 0 0 20px;
+            height: 100%;
+          "
+        >
           <TitleInfo :content="'产品统计'"></TitleInfo>
-          <div style="padding-top: 20px; display: flex; flex-wrap: wrap" v-loading="loadingOne">
+          <div
+            style="padding-top: 20px; display: flex; flex-wrap: wrap"
+            v-loading="loadingOne"
+          >
             <div style="width: 33%; padding: 0 20px 20px 0">
-              <div style="padding: 20px; background-color: #f4f4f5; border-radius: 5px; font-size: 12px !important; color: #909399 !important">
+              <div
+                style="
+                  padding: 20px;
+                  background-color: #f4f4f5;
+                  border-radius: 5px;
+                  font-size: 12px !important;
+                  color: #909399 !important;
+                "
+              >
                 <div style="margin-bottom: 20px; display: flex">
                   <div style="background: #0084ff; padding: 5px">
-                    <img style="width: 20px; height: 20px; border-radius: 5px" src="@/assets/images/portrait/iconm_kehd.png" alt="" />
+                    <img
+                      style="width: 20px; height: 20px; border-radius: 5px"
+                      src="@/assets/images/portrait/iconm_kehd.png"
+                      alt=""
+                    />
+                  </div>
+                  <div
+                    style="
+                      margin-left: 20px;
+                      height: 30px;
+                      line-height: 30px;
+                      font-weight: 700;
+                    "
+                  >
+                    总计
                   </div>
-                  <div style="margin-left: 20px; height: 30px; line-height: 30px; font-weight: 700">总计</div>
                 </div>
                 <div style="display: flex">
                   <div style="width: 50%">
                     <span>新增 (款)</span>
-                    <span style="color: black; font-weight: 700; margin-left: 20px">{{ allData.productStatistics.newTotal }}</span>
+                    <span
+                      style="color: black; font-weight: 700; margin-left: 20px"
+                      >{{ allData.productStatistics.newTotal }}</span
+                    >
                   </div>
                   <div style="width: 50%">
                     <span>总计 (款)</span>
-                    <span style="color: black; font-weight: 700; margin-left: 20px">{{ allData.productStatistics.total }}</span>
+                    <span
+                      style="color: black; font-weight: 700; margin-left: 20px"
+                      >{{ allData.productStatistics.total }}</span
+                    >
                   </div>
                 </div>
               </div>
             </div>
             <template v-if="productType && productType.length > 0">
-              <div style="width: 33%; padding: 0 20px 20px 0" v-for="(item, index) in productType" :key="index">
-                <div style="padding: 20px; background-color: #f4f4f5; border-radius: 5px; font-size: 12px !important; color: #909399 !important">
+              <div
+                style="width: 33%; padding: 0 20px 20px 0"
+                v-for="(item, index) in productType"
+                :key="index"
+              >
+                <div
+                  style="
+                    padding: 20px;
+                    background-color: #f4f4f5;
+                    border-radius: 5px;
+                    font-size: 12px !important;
+                    color: #909399 !important;
+                  "
+                >
                   <div style="margin-bottom: 20px; display: flex">
                     <div style="background: #0084ff; padding: 5px">
-                      <img style="width: 20px; height: 20px; border-radius: 5px" src="@/assets/images/portrait/iconm_kehd.png" alt="" />
+                      <img
+                        style="width: 20px; height: 20px; border-radius: 5px"
+                        src="@/assets/images/portrait/iconm_kehd.png"
+                        alt=""
+                      />
+                    </div>
+                    <div
+                      style="
+                        margin-left: 20px;
+                        height: 30px;
+                        line-height: 30px;
+                        font-weight: 700;
+                      "
+                    >
+                      {{ item.label }}
                     </div>
-                    <div style="margin-left: 20px; height: 30px; line-height: 30px; font-weight: 700">{{ item.label }}</div>
                   </div>
                   <div style="display: flex">
                     <div style="width: 50%">
                       <span>新增 (款)</span>
-                      <span style="color: black; font-weight: 700; margin-left: 20px">{{ getNum(item, "typeNewTotal") }}</span>
+                      <span
+                        style="
+                          color: black;
+                          font-weight: 700;
+                          margin-left: 20px;
+                        "
+                        >{{ getNum(item, "typeNewTotal") }}</span
+                      >
                     </div>
                     <div style="width: 50%">
                       <span>总计 (款)</span>
-                      <span style="color: black; font-weight: 700; margin-left: 20px">{{ getNum(item, "typeTotal") }}</span>
+                      <span
+                        style="
+                          color: black;
+                          font-weight: 700;
+                          margin-left: 20px;
+                        "
+                        >{{ getNum(item, "typeTotal") }}</span
+                      >
                     </div>
                   </div>
                 </div>
@@ -82,20 +171,49 @@
     </el-row>
     <el-row style="margin-top: 20px" type="flex">
       <el-col :span="12">
-        <div style="margin-right: 20px; background-color: white; padding: 20px; height: 100%">
-          <TitleInfo :content="'产品类型排行'"></TitleInfo>
+        <div
+          style="
+            margin-right: 20px;
+            background-color: white;
+            padding: 20px;
+            height: 100%;
+          "
+        >
+          <TitleInfo :content="'产品分类排行'"></TitleInfo>
           <div style="padding-top: 20px" v-loading="loadingTwo">
             <el-table
               :data="allData.productTypeRanking"
               :default-sort="{ prop: 'contractQuantity', order: 'descending' }"
               style="width: 100%"
               max-height="30vh"
-              @sort-change="sortChangeTwo">
+              @sort-change="sortChangeTwo"
+            >
+              <el-table-column type="index" width="50" />
               <el-table-column label="产品类型" prop="name" min-width="140" />
-              <el-table-column label="销售量" prop="contractQuantity" sortable width="120" />
-              <el-table-column label="销售额" prop="contractAmount" sortable width="120" />
-              <el-table-column label="采购量" prop="purchaseQuantity" sortable width="120" />
-              <el-table-column label="采购额" prop="purchaseAmount" sortable width="120" />
+              <el-table-column
+                label="销售量"
+                prop="contractQuantity"
+                sortable
+                width="120"
+              />
+              <el-table-column
+                label="销售额"
+                prop="contractAmount"
+                sortable
+                width="120"
+              />
+              <el-table-column
+                label="采购量"
+                prop="purchaseQuantity"
+                sortable
+                width="120"
+              />
+              <el-table-column
+                label="采购额"
+                prop="purchaseAmount"
+                sortable
+                width="120"
+              />
               <!-- <el-table-column label="生产总量" prop="name" sortable width="120" /> -->
             </el-table>
           </div>
@@ -109,13 +227,37 @@
               :data="allData.productRanking"
               :default-sort="{ prop: 'contractQuantity', order: 'descending' }"
               style="width: 100%"
+              border
               max-height="30vh"
-              @sort-change="sortChangeThree">
+              @sort-change="sortChangeThree"
+            >
+              <el-table-column type="index" width="50" />
               <el-table-column label="产品名称" prop="name" min-width="140" />
-              <el-table-column label="销售量" prop="contractQuantity" sortable width="120" />
-              <el-table-column label="销售额" prop="contractAmount" sortable width="120" />
-              <el-table-column label="采购量" prop="purchaseQuantity" sortable width="120" />
-              <el-table-column label="采购额" prop="purchaseAmount" sortable width="120" />
+              <el-table-column
+                label="销售量"
+                prop="contractQuantity"
+                sortable
+                width="120"
+              />
+              <el-table-column
+                label="销售额"
+                prop="contractAmount"
+                sortable
+                width="120"
+              />
+
+              <el-table-column
+                label="采购量"
+                prop="purchaseQuantity"
+                sortable
+                width="120"
+              />
+              <el-table-column
+                label="采购额"
+                prop="purchaseAmount"
+                sortable
+                width="120"
+              />
               <!-- <el-table-column label="生产总量" prop="name" sortable width="120" /> -->
             </el-table>
           </div>
@@ -153,12 +295,16 @@ const getDict = () => {
 };
 getDict();
 const getCountryData = () => {
-  proxy.post("/areaInfo/list", { parentId: "0" }).then((res) => {
+  proxy.post("/customizeArea/list", { parentId: "0" }).then((res) => {
     countryData.value = res;
     let endData = new Date();
     let beginDate = new Date();
-    beginDate.setFullYear(endData.getFullYear() - 1);
-    queryForm.timeArr = [proxy.parseTime(beginDate, "{y}-{m}-{d}"), proxy.parseTime(endData, "{y}-{m}-{d}")];
+    beginDate.setMonth(0);
+    beginDate.setDate(1);
+    queryForm.timeArr = [
+      proxy.parseTime(beginDate, "{y}-{m}-{d}"),
+      proxy.parseTime(endData, "{y}-{m}-{d}"),
+    ];
     queryForm.beginTime = queryForm.timeArr[0];
     queryForm.endTime = queryForm.timeArr[1];
     getData();
@@ -185,8 +331,13 @@ const allData = reactive({
 });
 const getNum = (item, label) => {
   let text = "";
-  if (allData.productStatistics.typeList && allData.productStatistics.typeList.length > 0) {
-    let data = allData.productStatistics.typeList.filter((row) => row.type === item.value);
+  if (
+    allData.productStatistics.typeList &&
+    allData.productStatistics.typeList.length > 0
+  ) {
+    let data = allData.productStatistics.typeList.filter(
+      (row) => row.type === item.value
+    );
     if (data && data.length > 0) {
       text = data[0][label];
     }
@@ -344,7 +495,6 @@ const getData = () => {
       optionTwo.data.series[0].data = [];
       optionTwo.data.series[1].data = [];
     }
-    console.log(optionTwo.data);
     myChartTwo.setOption(optionTwo.data);
     myChartTwo.resize();
   });
@@ -368,7 +518,7 @@ const getProductTypeRanking = () => {
   query.pageSize = 10;
   query.sort = productTypeRankingSort.value[productTypeRankingProp.value] || 10;
   query.orderBy = orderBy.value[productTypeRankingOrder.value] || 20;
-  proxy.post("/productInfo/productTypeRanking", query).then(
+  proxy.post("/productInfo/productClassifyRanking", query).then(
     (res) => {
       allData.productTypeRanking = res;
       setTimeout(() => {

+ 652 - 0
src/views/dataBoard/board/salesAnalysis/index - 副本.vue

@@ -0,0 +1,652 @@
+<template>
+  <div class="content">
+    <div class="bck">
+      <el-form :inline="true" :model="queryForm">
+        <!-- <el-form-item label="">
+          <el-date-picker
+            v-model="queryForm.aa"
+            type="month"
+            placeholder="月份"
+            @change="onQuery"
+          />
+        </el-form-item> -->
+        <el-form-item label="">
+          <el-radio-group v-model="queryForm.bb" size="large" @change="onQuery">
+            <el-radio-button label="本日" />
+            <el-radio-button label="本周" />
+            <el-radio-button label="本月" />
+            <el-radio-button label="今年" />
+            <el-radio-button label="其他" />
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item>
+          <el-date-picker
+            v-model="queryForm.timeArr"
+            type="daterange"
+            unlink-panels
+            range-separator="-"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            value-format="YYYY-MM-DD"
+            @change="onQuery"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="onQuery">搜索</el-button>
+          <el-button type="defualt" @click="onReset">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <div
+      class="bck statistics"
+      style="margin-top: 10px"
+      v-loading="statisticsLoading"
+    >
+      <div class="item first">
+        <div class="left_">
+          <div class="money">
+            {{ moneyFormat(allData.salesStatisticsData.amount, 2) }}
+          </div>
+          <div>销售额(¥)</div>
+        </div>
+        <div class="right_">
+          <div class="icon">
+            <img src="@/assets/images/portrait/icon_sales.png" alt="" />
+          </div>
+        </div>
+      </div>
+      <div class="item two">
+        <div class="left_">
+          <div class="money">
+            {{ allData.salesStatisticsData.contractCount }}
+          </div>
+          <div>订单(单)</div>
+        </div>
+        <div class="right_">
+          <div class="icon">
+            <img src="@/assets/images/portrait/icon_profits.png" alt="" />
+          </div>
+        </div>
+      </div>
+      <div class="item three">
+        <div class="left_">
+          <div class="money">
+            {{ allData.salesStatisticsData.customerCount }}
+          </div>
+          <div>下单客户数(人)</div>
+        </div>
+        <div class="right_">
+          <div class="icon">
+            <img src="@/assets/images/portrait/icon_email.png" alt="" />
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div
+      class="bck scatter"
+      style="margin-top: 10px"
+      v-loading="scatterLoading"
+    >
+      <el-row>
+        <el-col :span="8">
+          <TitleInfo :content="titleList[0]"></TitleInfo>
+          <div ref="echartDomOne" style="height: 40vh"></div>
+        </el-col>
+        <el-col :span="16">
+          <div ref="echartDomTwo" style="height: 40vh"></div>
+        </el-col>
+      </el-row>
+    </div>
+
+    <div style="margin-top: 10px">
+      <el-row>
+        <el-col :span="12" class="bck" v-loading="salesLoading">
+          <TitleInfo :content="titleList[1]"></TitleInfo>
+          <div ref="echartDomOne22" style="height: 30vh"></div>
+          <div style="margin-top: 10px">
+            <byTable
+              :hideSearch="true"
+              :tableHeight="300"
+              :source="sourceList.salesData"
+              :pagination="sourceList.salesPagination"
+              :config="salesConfig"
+              :loading="salesLoading"
+              highlight-current-row
+              :selectConfig="[]"
+            >
+            </byTable>
+          </div>
+        </el-col>
+        <el-col :span="12" style="padding-left: 10px">
+          <div class="bck" v-loading="salesmanSalesLoading">
+            <TitleInfo :content="titleList[2]"></TitleInfo>
+            <!-- <div ref="echartDomTwo" style="height: 30vh"></div> -->
+            <div style="margin-top: 10px">
+              <byTable
+                :hideSearch="true"
+                :tableHeight="300"
+                :source="sourceList.salesmanSalesData"
+                :pagination="sourceList.salesmanSalesPagination"
+                :config="salesmanSalesConfig"
+                :loading="salesmanSalesLoading"
+                highlight-current-row
+                :selectConfig="[]"
+              >
+              </byTable>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+
+    <div style="margin-top: 10px">
+      <el-row>
+        <el-col :span="12" class="bck" v-loading="productSalesLoading">
+          <TitleInfo :content="titleList[3]"></TitleInfo>
+          <div ref="echartDomThree" style="height: 30vh"></div>
+          <div style="margin-top: 10px">
+            <byTable
+              :hideSearch="true"
+              :tableHeight="300"
+              :source="sourceList.productSalesVolumeData"
+              :pagination="sourceList.productSalesVolumePagination"
+              :config="productSalesConfig"
+              :loading="productSalesLoading"
+              highlight-current-row
+              :selectConfig="[]"
+            >
+            </byTable>
+          </div>
+        </el-col>
+        <el-col :span="12" style="padding-left: 10px">
+          <div class="bck" v-loading="productSalesOneLoading">
+            <TitleInfo :content="titleList[4]"></TitleInfo>
+            <div ref="echartDomFour" style="height: 30vh"></div>
+            <div style="margin-top: 10px">
+              <byTable
+                :hideSearch="true"
+                :tableHeight="300"
+                :source="sourceList.productSalesVolumeOneData"
+                :pagination="sourceList.productSalesVolumeOnePagination"
+                :config="productSalesOneConfig"
+                :loading="productSalesOneLoading"
+                highlight-current-row
+                :selectConfig="[]"
+              >
+              </byTable>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import * as echarts from "echarts";
+
+import byTable from "@/components/byTable/index";
+import TitleInfo from "@/components/TitleInfo/index.vue";
+const titleList = [
+  "销售分布",
+  "客户分类",
+  "业务员销售趋势",
+  "商品销售额",
+  "商品销量",
+];
+const { proxy } = getCurrentInstance();
+
+const statisticsLoading = ref(false);
+const scatterLoading = ref(false);
+const salesLoading = ref(false);
+const salesmanSalesLoading = ref(false);
+const productSalesLoading = ref(false);
+const productSalesOneLoading = ref(false);
+
+const queryForm = reactive({
+  beginTime: "",
+  endTime: "",
+  timeArr: "",
+});
+const sourceList = ref({
+  scatterData: [],
+  scatterPagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+  },
+  salesData: [],
+  salesPagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+  },
+  salesmanSalesData: [],
+  salesmanSalesPagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+  },
+  productSalesVolumeData: [],
+  productSalesVolumePagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+  },
+  productSalesVolumeOneData: [],
+  productSalesVolumeOnePagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+  },
+});
+
+const allData = reactive({
+  salesStatisticsData: {},
+});
+//图表
+const echartDomOne = ref(null);
+const echartDomTwo = ref(null);
+const echartDomThree = ref(null);
+const echartDomFour = ref(null);
+let myChart = null;
+let myChartOne = null;
+let myChartTwo = null;
+let myChartThree = null;
+let myChartFour = null;
+const optionOne = reactive({
+  data: {
+    tooltip: {
+      trigger: "item",
+    },
+    legend: {
+      bottom: "0%",
+      left: "center",
+    },
+    series: [
+      {
+        name: '111',
+        type: 'pie',
+        radius: [20, 140],
+        center: ['50%', '50%'],
+        roseType: 'area',
+        itemStyle: {
+          borderRadius: 5
+        },
+        label: {
+          show: false,
+          position: "center",
+        },
+        emphasis: {
+          label: {
+            show: true,
+            fontSize: 20,
+            fontWeight: "bold",
+          },
+        },
+        labelLine: {
+          show: false,
+        },
+        data: [
+          { value: 1048, name: "中国" },
+          { value: 735, name: "美国" },
+          { value: 580, name: "韩国" },
+          { value: 484, name: "德国" },
+          { value: 300, name: "xxx" },
+        ],
+      },
+    ],
+  },
+});
+const optionTwo = reactive({
+  data:{
+    legend: {},
+    tooltip: {},
+    dataset: {
+      source: [
+        ['product', '订单量', '交易金额'],
+        ['Matcha Latte', 43.3, 85.8],
+        ['Milk Tea', 83.1, 73.4],
+        ['Cheese Cocoa', 86.4, 65.2],
+        ['Walnut Brownie', 72.4, 53.9]
+      ]
+    },
+    xAxis: { type: 'category' },
+    yAxis: {},
+    // Declare several bar series, each will be mapped
+    // to a column of dataset.source by default.
+    series: [{ type: 'bar' }, { type: 'bar' }]
+  }
+});
+const scatterConfig = computed(() => {
+  return [
+    {
+      type: "index",
+      attrs: {
+        label: "序列",
+        width: 80,
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "国家",
+        prop: "countryName",
+      },
+    },
+    {
+      attrs: {
+        label: "订单量(单)",
+        prop: "count",
+      },
+    },
+    {
+      attrs: {
+        label: "交易金额(¥)",
+        prop: "amount",
+      },
+      render(amount) {
+        return proxy.moneyFormat(amount, 2);
+      },
+    },
+  ];
+});
+const salesConfig = computed(() => {
+  return [
+    {
+      type: "index",
+      attrs: {
+        label: "序列",
+        width: 80,
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "客户",
+        prop: "corporationName",
+      },
+    },
+    {
+      attrs: {
+        label: "销售额(¥)",
+        prop: "amount",
+      },
+      render(amount) {
+        return proxy.moneyFormat(amount, 2);
+      },
+    },
+  ];
+});
+const salesmanSalesConfig = computed(() => {
+  return [
+    {
+      type: "index",
+      attrs: {
+        label: "序列",
+        width: 80,
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "业务员",
+        prop: "userName",
+      },
+    },
+    {
+      attrs: {
+        label: "销售额(¥)",
+        prop: "amount",
+      },
+      render(amount) {
+        return proxy.moneyFormat(amount, 2);
+      },
+    },
+  ];
+});
+const productSalesConfig = computed(() => {
+  return [
+    {
+      type: "index",
+      attrs: {
+        label: "序列",
+        width: 80,
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "商品名称",
+        prop: "productName",
+      },
+    },
+    {
+      attrs: {
+        label: "销售额(¥)",
+        prop: "amount",
+      },
+      render(amount) {
+        return proxy.moneyFormat(amount, 2);
+      },
+    },
+  ];
+});
+
+const productSalesOneConfig = computed(() => {
+  return [
+    {
+      type: "index",
+      attrs: {
+        label: "序列",
+        width: 80,
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "商品名称",
+        prop: "productName",
+      },
+    },
+    {
+      attrs: {
+        label: "销售量(个)",
+        prop: "quantity",
+      },
+      render(quantity) {
+        return proxy.moneyFormat(quantity, 2);
+      },
+    },
+  ];
+});
+const getData = () => {
+  statisticsLoading.value = true;
+  scatterLoading.value = true;
+  salesLoading.value = true;
+  salesmanSalesLoading.value = true;
+  productSalesLoading.value = true;
+  productSalesOneLoading.value = true;
+  proxy.post("/contract/salesStatistics", queryForm).then((res) => {
+    allData.salesStatisticsData = res;
+    setTimeout(() => {
+      statisticsLoading.value = false;
+    }, 200);
+  });
+  proxy.post("/contract/countrySalesStatistics", queryForm).then((res) => {
+    sourceList.value.scatterData = res;
+    // optionOne.data.series[0].data = res.map((x) => ({
+    //   value: x.amount,
+    //   name: x.countryName,
+    // }));
+    myChartOne.setOption(optionOne.data);
+    setTimeout(() => {
+      scatterLoading.value = false;
+    }, 200);
+    myChartTwo.setOption(optionTwo.data);
+    setTimeout(() => {
+      salesmanSalesLoading.value = false;
+    }, 200);
+  });
+
+  // proxy.post("/contract/customSalesStatistics", queryForm).then((res) => {
+  //   sourceList.value.salesData = res;
+  //   const option = { ...optionTwo };
+  //   // option.data.xAxis[0].data = res.map((x) => x.corporationName);
+  //   // option.data.series[0].data = res.map((x) => x.amount);
+  //   // option.data.series[0].name = "销售额(¥)";
+    
+  // });
+
+  // proxy.post("/contract/salesmanSalesStatistics", queryForm).then((res) => {
+  //   sourceList.value.salesmanSalesData = res;
+  //   const option = { ...optionTwo };
+  //   option.data.xAxis[0].data = res.map((x) => x.userName);
+  //   option.data.series[0].data = res.map((x) => x.amount);
+  //   option.data.series[0].name = "销售额(¥)";
+  //   myChartTwo.setOption(option.data);
+  //   setTimeout(() => {
+  //     salesmanSalesLoading.value = false;
+  //   }, 200);
+  // });
+
+  // proxy.post("/contract/productSalesStatistics", queryForm).then((res) => {
+  //   sourceList.value.productSalesVolumeData = res;
+  //   const option = { ...optionTwo };
+  //   option.data.xAxis[0].data = res.map((x) => x.productName);
+  //   option.data.series[0].data = res.map((x) => x.amount);
+  //   option.data.series[0].name = "销售额(¥)";
+  //   myChartThree.setOption(option.data);
+  //   setTimeout(() => {
+  //     productSalesLoading.value = false;
+  //   }, 200);
+  // });
+
+  // proxy.post("/contract/salesVolumeStatistics", queryForm).then((res) => {
+  //   sourceList.value.productSalesVolumeOneData = res;
+  //   const option = { ...optionTwo };
+  //   option.data.xAxis[0].data = res.map((x) => x.productName);
+  //   option.data.series[0].data = res.map((x) => x.quantity);
+  //   option.data.series[0].name = "销售量(个)";
+  //   myChartFour.setOption(option.data);
+  //   setTimeout(() => {
+  //     productSalesOneLoading.value = false;
+  //   }, 200);
+  // });
+};
+
+const onQuery = () => {
+  if (queryForm.timeArr.length > 1) {
+    queryForm.beginTime = queryForm.timeArr[0];
+    queryForm.endTime = queryForm.timeArr[1];
+  } else {
+    queryForm.beginTime = "";
+    queryForm.endTime = "";
+  }
+  getData();
+};
+
+const onReset = () => {
+  queryForm.timeArr = [];
+  onQuery();
+};
+
+onMounted(() => {
+  // 客户分类柱状图
+  myChartOne = echarts.init(echartDomOne.value);
+  window.addEventListener("resize", () => {
+    myChartOne.resize();
+  });
+  // 业务员销售趋势柱状图
+  myChartTwo = echarts.init(echartDomTwo.value);
+  window.addEventListener("resize", () => {
+    myChartTwo.resize();
+  });
+  //商品销售额柱状图
+  myChartThree = echarts.init(echartDomThree.value);
+  window.addEventListener("resize", () => {
+    myChartThree.resize();
+  });
+  // 商品销量柱状图
+  myChartFour = echarts.init(echartDomFour.value);
+  window.addEventListener("resize", () => {
+    myChartFour.resize();
+  });
+  getData();
+});
+</script>
+
+<style lang="scss" scoped>
+.content {
+  margin: 20px;
+}
+:deep(.el-form-item) {
+  margin-bottom: 0px;
+}
+.bck {
+  background-color: #fff;
+  padding: 15px;
+}
+.statistics {
+  display: flex;
+  // justify-content: space-around;
+  .item {
+    border-radius: 10px;
+    margin-right: 20px;
+    width: 13vw;
+    height: 70px;
+    padding: 10px;
+    box-sizing: border-box;
+    display: flex;
+    justify-content: space-between;
+    .left_ {
+      display: flex;
+      flex-direction: column;
+      justify-content: space-around;
+      .money {
+        font-weight: 700;
+        color: #333333;
+        font-size: 15px;
+      }
+    }
+    .right_ {
+      .icon {
+        img {
+          width: 20px;
+          height: 20px;
+          margin-top: 6px;
+        }
+        width: 34px;
+        height: 34px;
+        margin-top: 10px;
+        border-radius: 8px;
+        background-color: #ffffff;
+        text-align: center;
+        line-height: 34px;
+      }
+    }
+  }
+  .first {
+    background: linear-gradient(#c7e3fe, #dfecff);
+  }
+  .two {
+    background: linear-gradient(#eae8fb, #ded9ff);
+  }
+  .three {
+    background: linear-gradient(#fcf1e4, #fce5ca);
+  }
+  .four {
+    background: linear-gradient(#e2fbe8, #e2fbe8);
+  }
+  .five {
+    background: linear-gradient(#ffebe9, #ffebe9);
+  }
+}
+// .scatter {
+//   display: flex;
+// }
+.public {
+  display: flex;
+}
+</style>

+ 204 - 44
src/views/dataBoard/board/salesAnalysis/index.vue

@@ -42,47 +42,32 @@
       style="margin-top: 10px"
       v-loading="statisticsLoading"
     >
-      <div class="item first">
-        <div class="left_">
-          <div class="money">
-            {{ moneyFormat(allData.salesStatisticsData.amount, 2) }}
-          </div>
-          <div>销售额(¥)</div>
-        </div>
-        <div class="right_">
-          <div class="icon">
-            <img src="@/assets/images/portrait/icon_sales.png" alt="" />
-          </div>
-        </div>
-      </div>
-      <div class="item two">
-        <div class="left_">
-          <div class="money">
-            {{ allData.salesStatisticsData.contractCount }}
-          </div>
-          <div>订单(单)</div>
-        </div>
-        <div class="right_">
-          <div class="icon">
-            <img src="@/assets/images/portrait/icon_profits.png" alt="" />
-          </div>
-        </div>
-      </div>
-      <div class="item three">
-        <div class="left_">
-          <div class="money">
-            {{ allData.salesStatisticsData.customerCount }}
-          </div>
-          <div>下单客户数(人)</div>
-        </div>
-        <div class="right_">
-          <div class="icon">
-            <img src="@/assets/images/portrait/icon_email.png" alt="" />
-          </div>
-        </div>
+      <div class="stat-warp">
+        <ul>
+          <li class="theme1">
+            <div class="num">{{ moneyFormat(allData.salesStatisticsData.amount, 2) }}</div>
+            <div class="label">销售额(¥)</div>
+            <div class="icon-box">
+              <i class="icon iconfont icon-iconx_caiwgl"></i>
+            </div>
+          </li>
+          <li class="theme2">
+            <div class="num">{{ allData.salesStatisticsData.contractCount }}</div>
+            <div class="label">订单(单)</div>
+            <div class="icon-box">
+              <i class="icon iconfont icon-iconm_fukgl"></i>
+            </div>
+          </li>
+          <li class="theme3">
+            <div class="num"> {{ allData.salesStatisticsData.customerCount }}</div>
+            <div class="label">下单客户数(人)</div>
+            <div class="icon-box">
+              <i class="icon iconfont icon-iconm_unread"></i>
+            </div>
+          </li>
+        </ul>
       </div>
     </div>
-
     <div
       class="bck scatter"
       style="margin-top: 10px"
@@ -96,19 +81,31 @@
         <el-col :span="16">
           <byTable
             :hideSearch="true"
-            :tableHeight="250"
+            :tableHeight="tableHeight"
             :source="sourceList.scatterData"
             :pagination="sourceList.scatterPagination"
             :config="scatterConfig"
             :loading="scatterLoading"
             highlight-current-row
+            :hidePagination="true"
             :selectConfig="[]"
           >
+          <template #flag="{ item }">
+          <div>
+            <div>
+              <svg class="icon" aria-hidden="true">
+                  <use :xlink:href="'#' + item.countryIcon"></use>
+              </svg>
+              
+              {{ item.countryName }}
+            </div>
+            
+          </div>
+        </template>
           </byTable>
         </el-col>
       </el-row>
     </div>
-
     <div style="margin-top: 10px">
       <el-row>
         <el-col :span="12" class="bck" v-loading="salesLoading">
@@ -365,8 +362,8 @@ const scatterConfig = computed(() => {
     },
     {
       attrs: {
-        label: "国",
-        prop: "countryName",
+        label: "国",
+        slot: "flag",
       },
     },
     {
@@ -467,6 +464,9 @@ const productSalesConfig = computed(() => {
     },
   ];
 });
+const tableHeight = computed(() => {
+  return document.documentElement.clientHeight / 10 * 4;
+});
 
 const productSalesOneConfig = computed(() => {
   return [
@@ -510,6 +510,11 @@ const getData = () => {
   });
   proxy.post("/contract/countrySalesStatistics", queryForm).then((res) => {
     sourceList.value.scatterData = res;
+    //根据amount字段从大到小
+    res.sort((a, b) => b.amount - a.amount);
+    //截取前十条
+    if(res.length > 10) res = res.slice(0, 10);
+    
     optionOne.data.series[0].data = res.map((x) => ({
       value: x.amount,
       name: x.countryName,
@@ -610,7 +615,13 @@ onMounted(() => {
   window.addEventListener("resize", () => {
     myChartFour.resize();
   });
-  getData();
+  
+  let endData = new Date();
+  let beginDate = new Date();
+  beginDate.setMonth(0);
+  beginDate.setDate(1);
+  queryForm.timeArr = [proxy.parseTime(beginDate, "{y}-{m}-{d}"), proxy.parseTime(endData, "{y}-{m}-{d}")];
+  onQuery();
 });
 </script>
 
@@ -680,6 +691,155 @@ onMounted(() => {
     background: linear-gradient(#ffebe9, #ffebe9);
   }
 }
+.stat-warp {
+		background: #fff;
+		padding: 20px;
+		overflow: hidden;
+		position: relative;
+		border-radius: 5px;
+		
+		.title {
+			height: 60px;
+			select {
+				height: 60px;
+				border: none;
+				outline: none;
+				-webkit-appearance: none;
+				appearance: none;
+				font-size: 14px;
+				font-weight: bold;
+				background: url('@/assets/images/sanjiao.png') no-repeat right
+					center;
+				padding-right: 20px;
+			}
+			div {
+				height: 60px;
+				font-size: 14px;
+				font-weight: bold;
+				line-height: 60px;
+			}
+		}
+		ul {
+			padding: 0;
+			overflow: hidden;
+			margin: 0;
+			li {
+				list-style: none;
+				min-width: 285px;
+				box-sizing: border-box;
+				margin-right: 20px;
+				background: linear-gradient(360deg, #C7E3FE 0%, #DFECFF 100%);
+				float: left;
+				overflow: hidden;
+				padding: 20px;
+				color: #333333;
+				position: relative;
+				border-radius: 10px;
+				cursor: pointer;
+				.label {
+					font-size: 14px;
+					margin-top: 10px;
+				}
+				.label::before {
+					// width: 10px;
+					// height: 10px;
+					// content: '';
+					// border-radius: 50%;
+					// background: #0084ff;
+					// display: inline-block;
+					// margin-right: 10px;
+					
+				}
+				.num {
+					
+					font-size: 24px;
+					font-weight: bold;
+				}
+				.icon-box{
+					position: absolute;
+					height: 40px;
+					width: 40px;
+					right: 20px;
+					top: 20px;
+					background: #fff;
+					border-radius: 10px;
+					text-align: center;
+					line-height: 40px;
+					i{
+						font-size: 20px;
+						color:#0084FF;
+					}
+				}
+			}
+			//#F5F3FF #9E64ED
+			.theme2 {
+				background: linear-gradient(180deg, #EAE8FB 0%, #DED9FF 100%);
+				.label::before {
+					background: #7566F0;
+				}
+				.icon-box i {
+					color: #7566F0;
+				}
+			}
+			//#FFF1E1 #FF9315
+			.theme3 {
+				background: #fff1e1;
+				.label::before {
+					background: #ff9315;
+				}
+				.icon-box i {
+					color: #FF9315;
+				}
+			}
+			//#E2FBE8 #39C55A
+			.theme4 {
+				background: #e2fbe8;
+				.label::before {
+					background: #39c55a;
+				}
+			}
+			.theme5 {
+				background: #ffebe9;
+				.label::before {
+					background: #f94539;
+				}
+			}
+			.theme6 {
+				background: #e4f9f9;
+				.label::before {
+					background: #53cbcb;
+				}
+			}
+			.multi-data {
+				.label::before {
+					display: none;
+				}
+				.label {
+					font-size: 14px;
+					font-weight: bold;
+					color: #333;
+					margin-bottom: 8px;
+				}
+				.num-warp {
+					overflow: hidden;
+					.num-box {
+						float: left;
+						min-width: 80px;
+						margin-right: 20px;
+						.num-small {
+							font-size: 16px;
+							font-weight: bold;
+							margin-bottom: 8px;
+						}
+						.label-small {
+							color: #666;
+							font-size: 14px;
+						}
+					}
+				}
+			}
+		}
+	}
 // .scatter {
 //   display: flex;
 // }

+ 22 - 0
src/views/dataBoard/main/index.vue

@@ -0,0 +1,22 @@
+<template>
+  <div style="background: #fff">
+    <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
+      <el-tab-pane label="销售分析" name="one">
+        <SalesAnalysis></SalesAnalysis>
+      </el-tab-pane>
+      <el-tab-pane label="产品分析" name="two">
+        <ProductAnalysis></ProductAnalysis>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script setup>
+import SalesAnalysis from "./salesAnalysis.vue";
+import ProductAnalysis from "./productAnalysis.vue";
+
+const activeName = ref("one");
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 577 - 0
src/views/dataBoard/main/productAnalysis.vue

@@ -0,0 +1,577 @@
+<template>
+  <div class="content">
+    <div style="background-color: white; padding: 20px">
+      <el-form :inline="true" :model="queryForm">
+        <el-form-item label="国家">
+          <el-select
+            v-model="queryForm.countryId"
+            placeholder="请选择"
+            clearable
+            filterable
+            @change="onQuery"
+          >
+            <el-option
+              v-for="item in countryData"
+              :label="item.name"
+              :value="item.id"
+              :key="item.id"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="日期">
+          <el-date-picker
+            v-model="queryForm.timeArr"
+            type="daterange"
+            unlink-panels
+            range-separator="-"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            value-format="YYYY-MM-DD"
+            @change="onQuery"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="onQuery">搜索</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <el-row style="margin-top: 20px" type="flex">
+      <el-col :span="15">
+        <div
+          style="
+            margin-right: 20px;
+            background-color: white;
+            padding: 20px 0 0 20px;
+            height: 100%;
+          "
+        >
+          <TitleInfo :content="'产品统计'"></TitleInfo>
+          <div
+            style="padding-top: 20px; display: flex; flex-wrap: wrap"
+            v-loading="loadingOne"
+          >
+            <div style="width: 33%; padding: 0 20px 20px 0">
+              <div
+                style="
+                  padding: 20px;
+                  background-color: #f4f4f5;
+                  border-radius: 5px;
+                  font-size: 12px !important;
+                  color: #909399 !important;
+                "
+              >
+                <div style="margin-bottom: 20px; display: flex">
+                  <div style="background: #0084ff; padding: 5px">
+                    <img
+                      style="width: 20px; height: 20px; border-radius: 5px"
+                      src="@/assets/images/portrait/iconm_kehd.png"
+                      alt=""
+                    />
+                  </div>
+                  <div
+                    style="
+                      margin-left: 20px;
+                      height: 30px;
+                      line-height: 30px;
+                      font-weight: 700;
+                    "
+                  >
+                    总计
+                  </div>
+                </div>
+                <div style="display: flex">
+                  <div style="width: 50%">
+                    <span>新增 (款)</span>
+                    <span
+                      style="color: black; font-weight: 700; margin-left: 20px"
+                      >{{ allData.productStatistics.newTotal }}</span
+                    >
+                  </div>
+                  <div style="width: 50%">
+                    <span>总计 (款)</span>
+                    <span
+                      style="color: black; font-weight: 700; margin-left: 20px"
+                      >{{ allData.productStatistics.total }}</span
+                    >
+                  </div>
+                </div>
+              </div>
+            </div>
+            <template v-if="productType && productType.length > 0">
+              <div
+                style="width: 33%; padding: 0 20px 20px 0"
+                v-for="(item, index) in productType"
+                :key="index"
+              >
+                <div
+                  style="
+                    padding: 20px;
+                    background-color: #f4f4f5;
+                    border-radius: 5px;
+                    font-size: 12px !important;
+                    color: #909399 !important;
+                  "
+                >
+                  <div style="margin-bottom: 20px; display: flex">
+                    <div style="background: #0084ff; padding: 5px">
+                      <img
+                        style="width: 20px; height: 20px; border-radius: 5px"
+                        src="@/assets/images/portrait/iconm_kehd.png"
+                        alt=""
+                      />
+                    </div>
+                    <div
+                      style="
+                        margin-left: 20px;
+                        height: 30px;
+                        line-height: 30px;
+                        font-weight: 700;
+                      "
+                    >
+                      {{ item.label }}
+                    </div>
+                  </div>
+                  <div style="display: flex">
+                    <div style="width: 50%">
+                      <span>新增 (款)</span>
+                      <span
+                        style="
+                          color: black;
+                          font-weight: 700;
+                          margin-left: 20px;
+                        "
+                        >{{ getNum(item, "typeNewTotal") }}</span
+                      >
+                    </div>
+                    <div style="width: 50%">
+                      <span>总计 (款)</span>
+                      <span
+                        style="
+                          color: black;
+                          font-weight: 700;
+                          margin-left: 20px;
+                        "
+                        >{{ getNum(item, "typeTotal") }}</span
+                      >
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </template>
+          </div>
+        </div>
+      </el-col>
+      <el-col :span="9">
+        <div style="background-color: white; padding: 20px; height: 100%">
+          <TitleInfo :content="'产品分布'"></TitleInfo>
+          <div ref="mainOne" style="height: calc(100% - 24px)"></div>
+        </div>
+      </el-col>
+    </el-row>
+    <el-row style="margin-top: 20px" type="flex">
+      <el-col :span="12">
+        <div
+          style="
+            margin-right: 20px;
+            background-color: white;
+            padding: 20px;
+            height: 100%;
+          "
+        >
+          <TitleInfo :content="'产品分类排行'"></TitleInfo>
+          <div style="padding-top: 20px" v-loading="loadingTwo">
+            <el-table
+              :data="allData.productTypeRanking"
+              :default-sort="{ prop: 'contractQuantity', order: 'descending' }"
+              style="width: 100%"
+              max-height="30vh"
+              @sort-change="sortChangeTwo"
+            >
+              <el-table-column type="index" width="50" />
+              <el-table-column label="产品类型" prop="name" min-width="140" />
+              <el-table-column
+                label="销售量"
+                prop="contractQuantity"
+                sortable
+                width="120"
+              />
+              <el-table-column
+                label="销售额"
+                prop="contractAmount"
+                sortable
+                width="120"
+              />
+              <el-table-column
+                label="采购量"
+                prop="purchaseQuantity"
+                sortable
+                width="120"
+              />
+              <el-table-column
+                label="采购额"
+                prop="purchaseAmount"
+                sortable
+                width="120"
+              />
+              <!-- <el-table-column label="生产总量" prop="name" sortable width="120" /> -->
+            </el-table>
+          </div>
+        </div>
+      </el-col>
+      <el-col :span="12">
+        <div style="background-color: white; padding: 20px; height: 100%">
+          <TitleInfo :content="'产品排行'"></TitleInfo>
+          <div style="padding-top: 20px" v-loading="loadingThree">
+            <el-table
+              :data="allData.productRanking"
+              :default-sort="{ prop: 'contractQuantity', order: 'descending' }"
+              style="width: 100%"
+              border
+              max-height="30vh"
+              @sort-change="sortChangeThree"
+            >
+              <el-table-column type="index" width="50" />
+              <el-table-column label="产品名称" prop="name" min-width="140" />
+              <el-table-column
+                label="销售量"
+                prop="contractQuantity"
+                sortable
+                width="120"
+              />
+              <el-table-column
+                label="销售额"
+                prop="contractAmount"
+                sortable
+                width="120"
+              />
+
+              <el-table-column
+                label="采购量"
+                prop="purchaseQuantity"
+                sortable
+                width="120"
+              />
+              <el-table-column
+                label="采购额"
+                prop="purchaseAmount"
+                sortable
+                width="120"
+              />
+              <!-- <el-table-column label="生产总量" prop="name" sortable width="120" /> -->
+            </el-table>
+          </div>
+        </div>
+      </el-col>
+    </el-row>
+    <div style="margin-top: 20px; background-color: white; padding: 20px">
+      <TitleInfo :content="'销售趋势'"></TitleInfo>
+      <div ref="mainTwo" style="height: 40vh"></div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import * as echarts from "echarts";
+import TitleInfo from "@/components/TitleInfo/index.vue";
+import { reactive, ref } from "vue";
+
+const { proxy } = getCurrentInstance();
+const productType = ref([]);
+const countryData = ref([]);
+const queryForm = reactive({
+  countryId: "",
+  beginTime: "",
+  endTime: "",
+  timeArr: [],
+});
+const getDict = () => {
+  proxy.getDictOne(["product_type"]).then((res) => {
+    productType.value = res["product_type"].map((x) => ({
+      label: x.dictValue,
+      value: x.dictKey,
+    }));
+  });
+};
+getDict();
+const getCountryData = () => {
+  proxy.post("/customizeArea/list", { parentId: "0" }).then((res) => {
+    countryData.value = res;
+    let endData = new Date();
+    let beginDate = new Date();
+    beginDate.setMonth(0);
+    beginDate.setDate(1);
+    queryForm.timeArr = [
+      proxy.parseTime(beginDate, "{y}-{m}-{d}"),
+      proxy.parseTime(endData, "{y}-{m}-{d}"),
+    ];
+    queryForm.beginTime = queryForm.timeArr[0];
+    queryForm.endTime = queryForm.timeArr[1];
+    getData();
+  });
+};
+getCountryData();
+const onQuery = () => {
+  queryForm.beginTime = queryForm.timeArr[0];
+  queryForm.endTime = queryForm.timeArr[1];
+  getData();
+};
+const loadingOne = ref(null);
+const loadingTwo = ref(null);
+const loadingThree = ref(null);
+const allData = reactive({
+  productStatistics: {
+    newTotal: "",
+    total: "",
+    typeList: [],
+  },
+  productDistribution: [],
+  productTypeRanking: [],
+  productRanking: [],
+});
+const getNum = (item, label) => {
+  let text = "";
+  if (
+    allData.productStatistics.typeList &&
+    allData.productStatistics.typeList.length > 0
+  ) {
+    let data = allData.productStatistics.typeList.filter(
+      (row) => row.type === item.value
+    );
+    if (data && data.length > 0) {
+      text = data[0][label];
+    }
+  }
+  return text;
+};
+const optionOne = reactive({
+  data: {
+    tooltip: {
+      trigger: "item",
+    },
+    legend: {
+      top: "5%",
+      left: "center",
+    },
+    series: [
+      {
+        name: "产品分布",
+        type: "pie",
+        radius: ["40%", "70%"],
+        avoidLabelOverlap: false,
+        itemStyle: {
+          borderRadius: 10,
+          borderColor: "#fff",
+          borderWidth: 2,
+        },
+        label: {
+          show: false,
+          position: "center",
+        },
+        emphasis: {
+          label: {
+            show: true,
+            fontSize: 40,
+            fontWeight: "bold",
+          },
+        },
+        labelLine: {
+          show: false,
+        },
+        data: [],
+      },
+    ],
+  },
+});
+const optionTwo = reactive({
+  data: {
+    tooltip: {
+      trigger: "axis",
+    },
+    legend: {
+      data: ["月度销售额", "月成交单数"],
+    },
+    grid: {
+      left: "3%",
+      right: "4%",
+      bottom: "3%",
+      containLabel: true,
+    },
+    toolbox: {
+      feature: {
+        saveAsImage: {},
+      },
+    },
+    xAxis: {
+      type: "category",
+      boundaryGap: false,
+      data: [],
+    },
+    yAxis: {
+      type: "value",
+    },
+    series: [
+      {
+        name: "月度销售额",
+        type: "line",
+        data: [],
+      },
+      {
+        name: "月成交单数",
+        type: "line",
+        data: [],
+      },
+    ],
+  },
+});
+const mainOne = ref(null);
+const mainTwo = ref(null);
+let myChartOne = null;
+let myChartTwo = null;
+const productTypeRankingProp = ref("contractQuantity");
+const productTypeRankingOrder = ref("descending");
+const productTypeRankingSort = ref({
+  contractQuantity: 10,
+  contractAmount: 20,
+  purchaseQuantity: 30,
+  purchaseAmount: 40,
+});
+const productRankingProp = ref("contractQuantity");
+const productRankingOrder = ref("descending");
+const productRankingSort = ref({
+  contractQuantity: 10,
+  contractAmount: 20,
+  purchaseQuantity: 30,
+  purchaseAmount: 40,
+});
+const orderBy = ref({
+  ascending: 10,
+  descending: 20,
+});
+const getData = () => {
+  loadingOne.value = true;
+  proxy.post("/productInfo/productStatistics", queryForm).then(
+    (res) => {
+      allData.productStatistics = res;
+      setTimeout(() => {
+        loadingOne.value = false;
+      }, 200);
+    },
+    (err) => {
+      console.log(err);
+      setTimeout(() => {
+        loadingOne.value = false;
+      }, 200);
+    }
+  );
+  proxy.post("/productInfo/productDistribution", queryForm).then((res) => {
+    if (res && res.length > 0) {
+      optionOne.data.series[0].data = res.map((item) => {
+        return {
+          value: item.count,
+          name: item.name,
+          type: item.type,
+        };
+      });
+    } else {
+      optionOne.data.series[0].data = [];
+    }
+    myChartOne.setOption(optionOne.data);
+    myChartOne.resize();
+  });
+  proxy.post("/contract/saleTrend", queryForm).then((res) => {
+    if (res && res.length > 0) {
+      optionTwo.data.xAxis.data = res.map((item) => {
+        return item.month;
+      });
+      optionTwo.data.series[0].data = res.map((item) => {
+        return item.contractAmount;
+      });
+      optionTwo.data.series[1].data = res.map((item) => {
+        return item.contractQuantity;
+      });
+    } else {
+      optionTwo.data.xAxis.data = [];
+      optionTwo.data.series[0].data = [];
+      optionTwo.data.series[1].data = [];
+    }
+    myChartTwo.setOption(optionTwo.data);
+    myChartTwo.resize();
+  });
+  getProductTypeRanking();
+  getProductRanking();
+};
+const sortChangeTwo = ({ prop, order }) => {
+  productTypeRankingProp.value = prop;
+  productTypeRankingOrder.value = order;
+  getProductTypeRanking();
+};
+const sortChangeThree = ({ prop, order }) => {
+  productRankingProp.value = prop;
+  productRankingOrder.value = order;
+  getProductRanking();
+};
+const getProductTypeRanking = () => {
+  loadingTwo.value = true;
+  let query = proxy.deepClone(queryForm);
+  query.pageNum = 1;
+  query.pageSize = 10;
+  query.sort = productTypeRankingSort.value[productTypeRankingProp.value] || 10;
+  query.orderBy = orderBy.value[productTypeRankingOrder.value] || 20;
+  proxy.post("/productInfo/productClassifyRanking", query).then(
+    (res) => {
+      allData.productTypeRanking = res;
+      setTimeout(() => {
+        loadingTwo.value = false;
+      }, 200);
+    },
+    (err) => {
+      console.log(err);
+      setTimeout(() => {
+        loadingTwo.value = false;
+      }, 200);
+    }
+  );
+};
+const getProductRanking = () => {
+  loadingThree.value = true;
+  let query = proxy.deepClone(queryForm);
+  query.pageNum = 1;
+  query.pageSize = 10;
+  query.sort = productRankingSort.value[productRankingProp.value] || 10;
+  query.orderBy = orderBy.value[productRankingOrder.value] || 20;
+  proxy.post("/productInfo/productRanking", query).then(
+    (res) => {
+      allData.productRanking = res.rows;
+      setTimeout(() => {
+        loadingThree.value = false;
+      }, 200);
+    },
+    (err) => {
+      console.log(err);
+      setTimeout(() => {
+        loadingThree.value = false;
+      }, 200);
+    }
+  );
+};
+onMounted(() => {
+  myChartOne = echarts.init(mainOne.value);
+  window.addEventListener("resize", () => {
+    myChartOne.resize();
+  });
+  myChartTwo = echarts.init(mainTwo.value);
+  window.addEventListener("resize", () => {
+    myChartTwo.resize();
+  });
+});
+</script>
+
+<style lang="scss" scoped>
+.content {
+  margin: 20px;
+}
+:deep(.el-form-item) {
+  margin-bottom: 0px;
+}
+</style>

+ 911 - 0
src/views/dataBoard/main/salesAnalysis.vue

@@ -0,0 +1,911 @@
+<template>
+  <div class="content">
+    <div class="bck">
+      <el-form :inline="true" :model="queryForm">
+        <!-- <el-form-item label="">
+          <el-date-picker
+            v-model="queryForm.aa"
+            type="month"
+            placeholder="月份"
+            @change="onQuery"
+          />
+        </el-form-item>
+        <el-form-item label="">
+          <el-radio-group v-model="queryForm.bb" size="large" @change="onQuery">
+            <el-radio-button label="本日" />
+            <el-radio-button label="本周" />
+            <el-radio-button label="本月" />
+            <el-radio-button label="今年" />
+            <el-radio-button label="其他" />
+          </el-radio-group>
+        </el-form-item> -->
+        <el-form-item>
+          <el-date-picker
+            v-model="queryForm.timeArr"
+            type="daterange"
+            unlink-panels
+            range-separator="-"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            value-format="YYYY-MM-DD"
+            @change="onQuery"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="onQuery">搜索</el-button>
+          <el-button type="defualt" @click="onReset">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <div class="world" style="margin: 20px 0px">
+      <div ref="worldChart" style="height: 400px; width: 50%"></div>
+    </div>
+    <div
+      class="bck statistics"
+      style="margin-top: 10px"
+      v-loading="statisticsLoading"
+    >
+      <div class="stat-warp">
+        <ul>
+          <li class="theme1">
+            <div class="num">
+              {{ moneyFormat(allData.salesStatisticsData.amount, 2) }}
+            </div>
+            <div class="label">销售额(¥)</div>
+            <div class="icon-box">
+              <i class="icon iconfont icon-iconx_caiwgl"></i>
+            </div>
+          </li>
+          <li class="theme2">
+            <div class="num">
+              {{ allData.salesStatisticsData.contractCount }}
+            </div>
+            <div class="label">订单(单)</div>
+            <div class="icon-box">
+              <i class="icon iconfont icon-iconm_fukgl"></i>
+            </div>
+          </li>
+          <li class="theme3">
+            <div class="num">
+              {{ allData.salesStatisticsData.customerCount }}
+            </div>
+            <div class="label">下单客户数(人)</div>
+            <div class="icon-box">
+              <i class="icon iconfont icon-iconm_unread"></i>
+            </div>
+          </li>
+        </ul>
+      </div>
+    </div>
+    <div
+      class="bck scatter"
+      style="margin-top: 10px"
+      v-loading="scatterLoading"
+    >
+      <el-row>
+        <el-col :span="8">
+          <TitleInfo :content="titleList[0]"></TitleInfo>
+          <div ref="echartDom" style="height: 40vh"></div>
+        </el-col>
+        <el-col :span="16">
+          <byTable
+            :hideSearch="true"
+            :tableHeight="tableHeight"
+            :source="sourceList.scatterData"
+            :pagination="sourceList.scatterPagination"
+            :config="scatterConfig"
+            :loading="scatterLoading"
+            highlight-current-row
+            :hidePagination="true"
+            :selectConfig="[]"
+          >
+            <template #flag="{ item }">
+              <div>
+                <div>
+                  <svg class="icon" aria-hidden="true">
+                    <use :xlink:href="'#' + item.countryIcon"></use>
+                  </svg>
+
+                  {{ item.countryName }}
+                </div>
+              </div>
+            </template>
+          </byTable>
+        </el-col>
+      </el-row>
+    </div>
+    <div style="margin-top: 10px">
+      <el-row>
+        <el-col :span="12" class="bck" v-loading="salesLoading">
+          <TitleInfo :content="titleList[1]"></TitleInfo>
+          <div ref="echartDomOne" style="height: 30vh"></div>
+          <div style="margin-top: 10px">
+            <byTable
+              :hideSearch="true"
+              :tableHeight="300"
+              :source="sourceList.salesData"
+              :pagination="sourceList.salesPagination"
+              :config="salesConfig"
+              :loading="salesLoading"
+              highlight-current-row
+              :selectConfig="[]"
+            >
+            </byTable>
+          </div>
+        </el-col>
+        <el-col :span="12" style="padding-left: 10px">
+          <div class="bck" v-loading="salesmanSalesLoading">
+            <TitleInfo :content="titleList[2]"></TitleInfo>
+            <div ref="echartDomTwo" style="height: 30vh"></div>
+            <div style="margin-top: 10px">
+              <byTable
+                :hideSearch="true"
+                :tableHeight="300"
+                :source="sourceList.salesmanSalesData"
+                :pagination="sourceList.salesmanSalesPagination"
+                :config="salesmanSalesConfig"
+                :loading="salesmanSalesLoading"
+                highlight-current-row
+                :selectConfig="[]"
+              >
+              </byTable>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+
+    <div style="margin-top: 10px">
+      <el-row>
+        <el-col :span="12" class="bck" v-loading="productSalesLoading">
+          <TitleInfo :content="titleList[3]"></TitleInfo>
+          <div ref="echartDomThree" style="height: 30vh"></div>
+          <div style="margin-top: 10px">
+            <byTable
+              :hideSearch="true"
+              :tableHeight="300"
+              :source="sourceList.productSalesVolumeData"
+              :pagination="sourceList.productSalesVolumePagination"
+              :config="productSalesConfig"
+              :loading="productSalesLoading"
+              highlight-current-row
+              :selectConfig="[]"
+            >
+            </byTable>
+          </div>
+        </el-col>
+        <el-col :span="12" style="padding-left: 10px">
+          <div class="bck" v-loading="productSalesOneLoading">
+            <TitleInfo :content="titleList[4]"></TitleInfo>
+            <div ref="echartDomFour" style="height: 30vh"></div>
+            <div style="margin-top: 10px">
+              <byTable
+                :hideSearch="true"
+                :tableHeight="300"
+                :source="sourceList.productSalesVolumeOneData"
+                :pagination="sourceList.productSalesVolumeOnePagination"
+                :config="productSalesOneConfig"
+                :loading="productSalesOneLoading"
+                highlight-current-row
+                :selectConfig="[]"
+              >
+              </byTable>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import * as echarts from "echarts";
+// import "echarts-gl";
+// import "../../../world.js";
+
+import byTable from "@/components/byTable/index";
+import TitleInfo from "@/components/TitleInfo/index.vue";
+const titleList = [
+  "销售分布",
+  "客户分类",
+  "业务员销售趋势",
+  "商品销售额",
+  "商品销量",
+];
+const { proxy } = getCurrentInstance();
+
+const statisticsLoading = ref(false);
+const scatterLoading = ref(false);
+const salesLoading = ref(false);
+const salesmanSalesLoading = ref(false);
+const productSalesLoading = ref(false);
+const productSalesOneLoading = ref(false);
+
+const queryForm = reactive({
+  beginTime: "",
+  endTime: "",
+  timeArr: "",
+});
+const sourceList = ref({
+  scatterData: [],
+  scatterPagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+  },
+  salesData: [],
+  salesPagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+  },
+  salesmanSalesData: [],
+  salesmanSalesPagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+  },
+  productSalesVolumeData: [],
+  productSalesVolumePagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+  },
+  productSalesVolumeOneData: [],
+  productSalesVolumeOnePagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+  },
+});
+
+const allData = reactive({
+  salesStatisticsData: {},
+});
+//图表
+const echartDom = ref(null);
+const echartDomOne = ref(null);
+const echartDomTwo = ref(null);
+const echartDomThree = ref(null);
+const echartDomFour = ref(null);
+const worldChart = ref(null);
+let myChart = null;
+let myChartOne = null;
+let myChartTwo = null;
+let myChartThree = null;
+let myChartFour = null;
+let myWorldChart = null;
+
+const optionOne = reactive({
+  data: {
+    tooltip: {
+      trigger: "item",
+    },
+    legend: {
+      bottom: "0%",
+      left: "center",
+    },
+    series: [
+      {
+        name: "销售分析",
+        type: "pie",
+        radius: ["30%", "70%"],
+        avoidLabelOverlap: false,
+        itemStyle: {
+          borderRadius: 10,
+          borderColor: "#fff",
+          borderWidth: 2,
+        },
+        label: {
+          show: false,
+          position: "center",
+        },
+        emphasis: {
+          label: {
+            show: true,
+            fontSize: 20,
+            fontWeight: "bold",
+          },
+        },
+        labelLine: {
+          show: false,
+        },
+        data: [
+          { value: 1048, name: "Search Engine" },
+          { value: 735, name: "Direct" },
+          { value: 580, name: "Email" },
+          { value: 484, name: "Union Ads" },
+          { value: 300, name: "Video Ads" },
+        ],
+      },
+    ],
+  },
+});
+const optionTwo = reactive({
+  data: {
+    tooltip: {
+      trigger: "axis",
+      axisPointer: {
+        type: "shadow",
+      },
+    },
+    grid: {
+      left: "1%",
+      right: "1%",
+      bottom: "1%",
+      top: "10%",
+      containLabel: true,
+    },
+    xAxis: [
+      {
+        type: "category",
+        data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
+        axisTick: {
+          alignWithLabel: true,
+        },
+      },
+    ],
+    yAxis: [
+      {
+        type: "value",
+      },
+    ],
+    series: [
+      {
+        name: "名称",
+        type: "bar",
+        barWidth: "25%",
+        data: [10, 52, 200, 334, 390, 330, 220],
+        itemStyle: {
+          color: "#0084FF",
+        },
+      },
+    ],
+  },
+});
+const worldOption = ref({
+  backgroundColor: "#fff",
+  geo: {
+    map: "world",
+    roam: false,
+    label: {
+      emphasis: {
+        show: false,
+      },
+    },
+    silent: true,
+    itemStyle: {
+      normal: {
+        areaColor: "#323c48",
+      },
+      emphasis: {
+        areaColor: "#2a333d",
+      },
+    },
+    zoom: 1.2,
+  },
+  series: [
+    {
+      name: "弱",
+      type: "scatterGL",
+      progressive: 1e6,
+      coordinateSystem: "geo",
+      symbolSize: 2.3,
+      zoomScale: 0.002,
+      blendMode: "lighter",
+      large: true,
+      itemStyle: {
+        color: "#940819",
+      },
+      postEffect: {
+        enable: true,
+      },
+      silent: true,
+      dimensions: ["lng", "lat"],
+      data: new Float32Array(),
+    },
+  ],
+});
+const scatterConfig = computed(() => {
+  return [
+    {
+      type: "index",
+      attrs: {
+        label: "序列",
+        width: 80,
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "国旗",
+        slot: "flag",
+      },
+    },
+    {
+      attrs: {
+        label: "订单量(单)",
+        prop: "count",
+      },
+    },
+    {
+      attrs: {
+        label: "交易金额(¥)",
+        prop: "amount",
+      },
+      render(amount) {
+        return proxy.moneyFormat(amount, 2);
+      },
+    },
+  ];
+});
+const salesConfig = computed(() => {
+  return [
+    {
+      type: "index",
+      attrs: {
+        label: "序列",
+        width: 80,
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "客户",
+        prop: "corporationName",
+      },
+    },
+    {
+      attrs: {
+        label: "销售额(¥)",
+        prop: "amount",
+      },
+      render(amount) {
+        return proxy.moneyFormat(amount, 2);
+      },
+    },
+  ];
+});
+const salesmanSalesConfig = computed(() => {
+  return [
+    {
+      type: "index",
+      attrs: {
+        label: "序列",
+        width: 80,
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "业务员",
+        prop: "userName",
+      },
+    },
+    {
+      attrs: {
+        label: "销售额(¥)",
+        prop: "amount",
+      },
+      render(amount) {
+        return proxy.moneyFormat(amount, 2);
+      },
+    },
+  ];
+});
+const productSalesConfig = computed(() => {
+  return [
+    {
+      type: "index",
+      attrs: {
+        label: "序列",
+        width: 80,
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "商品名称",
+        prop: "productName",
+      },
+    },
+    {
+      attrs: {
+        label: "销售额(¥)",
+        prop: "amount",
+      },
+      render(amount) {
+        return proxy.moneyFormat(amount, 2);
+      },
+    },
+  ];
+});
+const tableHeight = computed(() => {
+  return (document.documentElement.clientHeight / 10) * 4;
+});
+
+const productSalesOneConfig = computed(() => {
+  return [
+    {
+      type: "index",
+      attrs: {
+        label: "序列",
+        width: 80,
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "商品名称",
+        prop: "productName",
+      },
+    },
+    {
+      attrs: {
+        label: "销售量(个)",
+        prop: "quantity",
+      },
+      render(quantity) {
+        return proxy.moneyFormat(quantity, 2);
+      },
+    },
+  ];
+});
+const getData = () => {
+  statisticsLoading.value = true;
+  scatterLoading.value = true;
+  salesLoading.value = true;
+  salesmanSalesLoading.value = true;
+  productSalesLoading.value = true;
+  productSalesOneLoading.value = true;
+  proxy.post("/contract/salesStatistics", queryForm).then((res) => {
+    allData.salesStatisticsData = res;
+    setTimeout(() => {
+      statisticsLoading.value = false;
+    }, 200);
+  });
+  proxy.post("/contract/countrySalesStatistics", queryForm).then((res) => {
+    sourceList.value.scatterData = res;
+    //根据amount字段从大到小
+    res.sort((a, b) => b.amount - a.amount);
+    //截取前十条
+    if (res.length > 10) res = res.slice(0, 10);
+
+    optionOne.data.series[0].data = res.map((x) => ({
+      value: x.amount,
+      name: x.countryName,
+    }));
+    myChart.setOption(optionOne.data);
+    setTimeout(() => {
+      scatterLoading.value = false;
+    }, 200);
+  });
+
+  proxy.post("/contract/customSalesStatistics", queryForm).then((res) => {
+    sourceList.value.salesData = res;
+    const option = { ...optionTwo };
+    option.data.xAxis[0].data = res.map((x) => x.corporationName);
+    option.data.series[0].data = res.map((x) => x.amount);
+    option.data.series[0].name = "销售额(¥)";
+    myChartOne.setOption(option.data);
+    setTimeout(() => {
+      salesLoading.value = false;
+    }, 200);
+  });
+
+  proxy.post("/contract/salesmanSalesStatistics", queryForm).then((res) => {
+    sourceList.value.salesmanSalesData = res;
+    const option = { ...optionTwo };
+    option.data.xAxis[0].data = res.map((x) => x.userName);
+    option.data.series[0].data = res.map((x) => x.amount);
+    option.data.series[0].name = "销售额(¥)";
+    myChartTwo.setOption(option.data);
+    setTimeout(() => {
+      salesmanSalesLoading.value = false;
+    }, 200);
+  });
+
+  proxy.post("/contract/productSalesStatistics", queryForm).then((res) => {
+    sourceList.value.productSalesVolumeData = res;
+    const option = { ...optionTwo };
+    option.data.xAxis[0].data = res.map((x) => x.productName);
+    option.data.series[0].data = res.map((x) => x.amount);
+    option.data.series[0].name = "销售额(¥)";
+    myChartThree.setOption(option.data);
+    setTimeout(() => {
+      productSalesLoading.value = false;
+    }, 200);
+  });
+
+  proxy.post("/contract/salesVolumeStatistics", queryForm).then((res) => {
+    sourceList.value.productSalesVolumeOneData = res;
+    const option = { ...optionTwo };
+    option.data.xAxis[0].data = res.map((x) => x.productName);
+    option.data.series[0].data = res.map((x) => x.quantity);
+    option.data.series[0].name = "销售量(个)";
+    myChartFour.setOption(option.data);
+    setTimeout(() => {
+      productSalesOneLoading.value = false;
+    }, 200);
+  });
+};
+
+const onQuery = () => {
+  if (queryForm.timeArr.length > 1) {
+    queryForm.beginTime = queryForm.timeArr[0];
+    queryForm.endTime = queryForm.timeArr[1];
+  } else {
+    queryForm.beginTime = "";
+    queryForm.endTime = "";
+  }
+  getData();
+};
+
+const onReset = () => {
+  queryForm.timeArr = [];
+  onQuery();
+};
+
+onMounted(() => {
+  myChart = echarts.init(echartDom.value);
+  window.addEventListener("resize", () => {
+    myChart.resize();
+  });
+  // 客户分类柱状图
+  myChartOne = echarts.init(echartDomOne.value);
+  window.addEventListener("resize", () => {
+    myChartOne.resize();
+  });
+  // 业务员销售趋势柱状图
+  myChartTwo = echarts.init(echartDomTwo.value);
+  window.addEventListener("resize", () => {
+    myChartTwo.resize();
+  });
+  //商品销售额柱状图
+  myChartThree = echarts.init(echartDomThree.value);
+  window.addEventListener("resize", () => {
+    myChartThree.resize();
+  });
+  // 商品销量柱状图
+  myChartFour = echarts.init(echartDomFour.value);
+  window.addEventListener("resize", () => {
+    myChartFour.resize();
+  });
+  // 世界图表
+  myWorldChart = echarts.init(worldChart.value);
+  // myWorldChart.setOption(worldOption.value);
+  window.addEventListener("resize", () => {
+    myWorldChart.resize();
+  });
+
+  let endData = new Date();
+  let beginDate = new Date();
+  beginDate.setMonth(0);
+  beginDate.setDate(1);
+  queryForm.timeArr = [
+    proxy.parseTime(beginDate, "{y}-{m}-{d}"),
+    proxy.parseTime(endData, "{y}-{m}-{d}"),
+  ];
+  onQuery();
+});
+</script>
+
+<style lang="scss" scoped>
+.content {
+  margin: 20px;
+}
+:deep(.el-form-item) {
+  margin-bottom: 0px;
+}
+.bck {
+  background-color: #fff;
+  padding: 15px;
+}
+.statistics {
+  display: flex;
+  // justify-content: space-around;
+  .item {
+    border-radius: 10px;
+    margin-right: 20px;
+    width: 13vw;
+    height: 70px;
+    padding: 10px;
+    box-sizing: border-box;
+    display: flex;
+    justify-content: space-between;
+    .left_ {
+      display: flex;
+      flex-direction: column;
+      justify-content: space-around;
+      .money {
+        font-weight: 700;
+        color: #333333;
+        font-size: 15px;
+      }
+    }
+    .right_ {
+      .icon {
+        img {
+          width: 20px;
+          height: 20px;
+          margin-top: 6px;
+        }
+        width: 34px;
+        height: 34px;
+        margin-top: 10px;
+        border-radius: 8px;
+        background-color: #ffffff;
+        text-align: center;
+        line-height: 34px;
+      }
+    }
+  }
+  .first {
+    background: linear-gradient(#c7e3fe, #dfecff);
+  }
+  .two {
+    background: linear-gradient(#eae8fb, #ded9ff);
+  }
+  .three {
+    background: linear-gradient(#fcf1e4, #fce5ca);
+  }
+  .four {
+    background: linear-gradient(#e2fbe8, #e2fbe8);
+  }
+  .five {
+    background: linear-gradient(#ffebe9, #ffebe9);
+  }
+}
+.stat-warp {
+  background: #fff;
+  padding: 20px;
+  overflow: hidden;
+  position: relative;
+  border-radius: 5px;
+
+  .title {
+    height: 60px;
+    select {
+      height: 60px;
+      border: none;
+      outline: none;
+      -webkit-appearance: none;
+      appearance: none;
+      font-size: 14px;
+      font-weight: bold;
+      background: url("@/assets/images/sanjiao.png") no-repeat right center;
+      padding-right: 20px;
+    }
+    div {
+      height: 60px;
+      font-size: 14px;
+      font-weight: bold;
+      line-height: 60px;
+    }
+  }
+  ul {
+    padding: 0;
+    overflow: hidden;
+    margin: 0;
+    li {
+      list-style: none;
+      min-width: 285px;
+      box-sizing: border-box;
+      margin-right: 20px;
+      background: linear-gradient(360deg, #c7e3fe 0%, #dfecff 100%);
+      float: left;
+      overflow: hidden;
+      padding: 20px;
+      color: #333333;
+      position: relative;
+      border-radius: 10px;
+      cursor: pointer;
+      .label {
+        font-size: 14px;
+        margin-top: 10px;
+      }
+      .label::before {
+        // width: 10px;
+        // height: 10px;
+        // content: '';
+        // border-radius: 50%;
+        // background: #0084ff;
+        // display: inline-block;
+        // margin-right: 10px;
+      }
+      .num {
+        font-size: 24px;
+        font-weight: bold;
+      }
+      .icon-box {
+        position: absolute;
+        height: 40px;
+        width: 40px;
+        right: 20px;
+        top: 20px;
+        background: #fff;
+        border-radius: 10px;
+        text-align: center;
+        line-height: 40px;
+        i {
+          font-size: 20px;
+          color: #0084ff;
+        }
+      }
+    }
+    //#F5F3FF #9E64ED
+    .theme2 {
+      background: linear-gradient(180deg, #eae8fb 0%, #ded9ff 100%);
+      .label::before {
+        background: #7566f0;
+      }
+      .icon-box i {
+        color: #7566f0;
+      }
+    }
+    //#FFF1E1 #FF9315
+    .theme3 {
+      background: #fff1e1;
+      .label::before {
+        background: #ff9315;
+      }
+      .icon-box i {
+        color: #ff9315;
+      }
+    }
+    //#E2FBE8 #39C55A
+    .theme4 {
+      background: #e2fbe8;
+      .label::before {
+        background: #39c55a;
+      }
+    }
+    .theme5 {
+      background: #ffebe9;
+      .label::before {
+        background: #f94539;
+      }
+    }
+    .theme6 {
+      background: #e4f9f9;
+      .label::before {
+        background: #53cbcb;
+      }
+    }
+    .multi-data {
+      .label::before {
+        display: none;
+      }
+      .label {
+        font-size: 14px;
+        font-weight: bold;
+        color: #333;
+        margin-bottom: 8px;
+      }
+      .num-warp {
+        overflow: hidden;
+        .num-box {
+          float: left;
+          min-width: 80px;
+          margin-right: 20px;
+          .num-small {
+            font-size: 16px;
+            font-weight: bold;
+            margin-bottom: 8px;
+          }
+          .label-small {
+            color: #666;
+            font-size: 14px;
+          }
+        }
+      }
+    }
+  }
+}
+// .scatter {
+//   display: flex;
+// }
+.public {
+  display: flex;
+}
+</style>

File diff suppressed because it is too large
+ 19 - 0
src/world.js


Some files were not shown because too many files changed in this diff