Prechádzať zdrojové kódy

数据看板-产品分析页面

lxf 1 rok pred
rodič
commit
5632de24d2

BIN
public/img/logo2.png


+ 427 - 0
src/views/dataBoard/board/productAnalysis/index.vue

@@ -0,0 +1,427 @@
+<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="请选择" @change="onQuery">
+            <el-option v-for="item in countryData" :label="item.chineseName" :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 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%"
+              max-height="30vh"
+              @sort-change="sortChangeThree">
+              <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("/areaInfo/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}")];
+    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 = [];
+    }
+    console.log(optionTwo.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 = JSON.parse(JSON.stringify(queryForm));
+  query.pageNum = 1;
+  query.pageSize = 10;
+  query.sort = productTypeRankingSort.value[productTypeRankingProp.value] || 10;
+  query.orderBy = orderBy.value[productTypeRankingOrder.value] || 20;
+  proxy.post("/productInfo/productTypeRanking", 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 = JSON.parse(JSON.stringify(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>