From 8fc09566cc3ee8d0dae10992de31d0f09f257bb4 Mon Sep 17 00:00:00 2001
From: zhang_12370 <z2864490065@outlook.com>
Date: 星期二, 08 七月 2025 17:58:04 +0800
Subject: [PATCH] 1、删除通过审核进入正式库存的采购记录,页面弹出报错信息 2、开发首页模块 3、开发设备领用记录 4、调整生产明细不能为负数 5、修复采购登记人匹配问题

---
 src/views/index.vue |  725 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 721 insertions(+), 4 deletions(-)

diff --git a/src/views/index.vue b/src/views/index.vue
index c57322c..473aff0 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -1,14 +1,731 @@
 <template>
-  <div class="app-container">
-  
+  <div class="dashboard">
+    <!-- 椤堕儴缁熻鍗$墖 -->
+    <div class="top-cards">
+      <div class="stat-card revenue">
+        <div class="card-icon">
+          <i class="el-icon-money"></i>
+        </div>
+        <div class="card-content">
+          <div class="card-title">钀ユ敹閲戦</div>
+          <div class="card-value">
+            楼{{
+              homePageData.revenueAmount
+                ? formatThousand(homePageData.revenueAmount)
+                : "--"
+            }}
+          </div>
+          <div class="card-trend" v-if="homePageData.trend == '+'">
+            <span class="trend-label">杈冩槰鏃�</span>
+            <span class="trend-value up">+ {{ homePageData.changeRate }}</span>
+          </div>
+          <div class="card-trend" v-if="homePageData.trend == '-'">
+            <span class="trend-label">杈冩槰鏃�</span>
+            <span class="trend-value down"
+              >- {{ homePageData.changeRate }}</span
+            >
+          </div>
+        </div>
+      </div>
+
+      <div class="stat-card supply">
+        <div class="card-icon">
+          <i class="el-icon-truck"></i>
+        </div>
+        <div class="card-content">
+          <div class="card-title">渚涘簲閲�</div>
+          <div class="card-value">
+            {{
+              homePageData.saleQuantity
+                ? formatThousand(homePageData.saleQuantity)
+                : "--"
+            }}鍚�
+          </div>
+          <div class="card-trend" v-if="homePageData.trendQuantity == '+'">
+            <span class="trend-label">杈冩槰鏃�</span>
+            <span class="trend-value up"
+              >+ {{ homePageData.saleQuantityRate }}</span
+            >
+          </div>
+          <div class="card-trend" v-if="homePageData.trendQuantity == '-'">
+            <span class="trend-label">杈冩槰鏃�</span>
+            <span class="trend-value down"
+              >- {{ homePageData.saleQuantityRate }}</span
+            >
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 涓棿鍥捐〃鍖哄煙 -->
+    <div class="chart-section">
+      <div class="chart-container">
+        <div class="chart-title">钀ユ敹鍒嗗竷</div>
+        <div ref="pieChart" class="chart-content pie-chart"></div>
+      </div>
+
+      <div class="chart-container">
+        <div class="chart-title">
+          <span>渚涘簲閲忚秼鍔�</span>
+          <div>
+            <el-date-picker
+              :locale="zhCN"
+              v-model="selectMonth"
+              type="monthrange"
+              placeholder="閫夋嫨鏃ユ湡"
+              format="YYYY/MM"
+              value-format="YYYY-MM"
+              range-separator="鑷�"
+              start-placeholder="寮�濮嬫棩鏈�"
+              end-placeholder="缁撴潫鏃ユ湡"
+              @change="searchMonth"
+            />
+          </div>
+        </div>
+        <div ref="areaChart" class="chart-content area-chart"></div>
+      </div>
+    </div>
+
+    <!-- 搴曢儴涓夋爮甯冨眬 -->
+    <div class="bottom-section">
+      <!-- 搴撳瓨缁熻 -->
+      <div class="bottom-card inventory">
+        <div class="card-header">
+          <h3>搴撳瓨缁熻</h3>
+        </div>
+        <div class="inventory-items">
+          <div class="inventory-item" v-for="(item, index) in inventoryList.Yvalues" :key="index">
+            <div class="item-name">{{ inventoryList.Xkeys[index]? inventoryList.Xkeys[index] : "--"}}</div>
+            <div class="item-value">{{ item ? formatThousand(item) : "0" }}</div>
+            <div class="item-status">鍚�</div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 鏌辩姸鍥� -->
+      <div class="bottom-card chart">
+        <div class="card-header">
+          <h3>鏈堝害瀵规瘮</h3>
+        </div>
+        <div ref="barChart" class="chart-content bar-chart"></div>
+      </div>
+
+      <!-- 閿�鍞暟鎹〃鏍� -->
+      <div class="bottom-card table">
+        <div class="card-header">
+          <h3>閿�鍞暟鎹�</h3>
+        </div>
+        <el-table
+          :data="salesData"
+          style="width: 100%"
+          :header-cell-style="tableHeaderStyle"
+        >
+          <el-table-column
+            prop="product"
+            label="浜у搧"
+            mini-width="50"
+          ></el-table-column>
+          <el-table-column
+            prop="quantity"
+            label="鏁伴噺"
+            mini-width="50"
+          ></el-table-column>
+          <el-table-column
+            prop="amount"
+            label="閲戦"
+            mini-width="50"
+          ></el-table-column>
+        </el-table>
+      </div>
+    </div>
   </div>
 </template>
 
-<script setup name="Index">
+<!-- 鍒犻櫎澶氫綑鐨� script 缁撴潫鏍囩 -->
+<script setup>
+import { getCoalInfo, getYearlySales } from "@/api/home/index";
+import { ref, onMounted, nextTick } from "vue";
+import zhCn from "element-plus/dist/locale/zh-cn.mjs";
 
+// 鍏煎妯℃澘鍙橀噺鍚嶏紝鏆撮湶缁欐ā鏉夸娇鐢�
+const zhCN = zhCn;
+import * as echarts from "echarts";
+
+const homePageData = ref({});
+const selectMonth = ref([]);
+
+// 鐢熸垚鏃犻檺闅忔満棰滆壊锛圚SL绠楁硶淇濊瘉楂樿鲸璇嗗害銆佹煍鍜屼笉鍒虹溂锛�
+function generateRandomColors(count = 10) {
+  const colors = [];
+  const goldenAngle = 137.508; // 榛勯噾瑙掑害锛屼繚璇侀鑹插垎甯冨潎鍖�
+
+  for (let i = 0; i < count; i++) {
+    // 浣跨敤榛勯噾瑙掑害鍒嗗壊纭繚棰滆壊宸紓澶�
+    const hue = (i * goldenAngle) % 360;
+
+    // 楗卞拰搴︼細40-70% 閬垮厤杩囦簬椴滆壋
+    const saturation = 40 + Math.random() * 30;
+
+    // 鏄庡害锛�45-75% 閬垮厤杩囨殫鎴栬繃浜�
+    const lightness = 45 + Math.random() * 30;
+
+    colors.push(
+      `hsl(${Math.round(hue)}, ${Math.round(saturation)}%, ${Math.round(
+        lightness
+      )}%)`
+    );
+  }
+
+  return colors;
+}
+
+// HSL杞�16杩涘埗锛堝彲閫夛紝濡傛灉闇�瑕乭ex鏍煎紡锛�
+function hslToHex(hsl) {
+  const match = hsl.match(/hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)/);
+  if (!match) return hsl;
+
+  const h = parseInt(match[1]) / 360;
+  const s = parseInt(match[2]) / 100;
+  const l = parseInt(match[3]) / 100;
+
+  const hue2rgb = (p, q, t) => {
+    if (t < 0) t += 1;
+    if (t > 1) t -= 1;
+    if (t < 1 / 6) return p + (q - p) * 6 * t;
+    if (t < 1 / 2) return q;
+    if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
+    return p;
+  };
+
+  let r, g, b;
+  if (s === 0) {
+    r = g = b = l;
+  } else {
+    const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+    const p = 2 * l - q;
+    r = hue2rgb(p, q, h + 1 / 3);
+    g = hue2rgb(p, q, h);
+    b = hue2rgb(p, q, h - 1 / 3);
+  }
+
+  const toHex = (c) => {
+    const hex = Math.round(c * 255).toString(16);
+    return hex.length === 1 ? "0" + hex : hex;
+  };
+
+  return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
+}
+
+// 渚挎嵎鏂规硶锛氱洿鎺ヨ幏鍙�16杩涘埗棰滆壊鏁扮粍
+function getRandomHexColors(count = 1) {
+  return generateRandomColors(count).map(hslToHex);
+}
+
+// 鍗冨垎浣嶆牸寮忓寲鍑芥暟
+function formatThousand(num) {
+  if (typeof num === "number") return num.toLocaleString();
+  if (typeof num === "string") {
+    const n = Number(num.replace(/,/g, ""));
+    if (isNaN(n)) return num;
+    return n.toLocaleString();
+  }
+  return num;
+}
+
+// 閿�鍞暟鎹師濮�
+const salesData = [
+  { product: "鍘熺叅", quantity: "1234鍚�", amount: "楼456789", status: "宸插畬鎴�" },
+  { product: "绮剧叅", quantity: "567鍚�", amount: "楼234567", status: "宸插畬鎴�" },
+  { product: "鐒︾叅", quantity: "890鍚�", amount: "楼345678", status: "杩涜涓�" },
+  { product: "鍧楃叅", quantity: "432鍚�", amount: "楼123456", status: "宸插畬鎴�" },
+  { product: "鐓ゆ偿", quantity: "20000鍚�", amount: "楼234567", status: "杩涜涓�" },
+];
+
+const tableHeaderStyle = {
+  backgroundColor: "#f5f7fa",
+  color: "#606266",
+  fontSize: "12px",
+};
+
+// 鍥捐〃ref
+const pieChart = ref(null);
+const areaChart = ref(null);
+const barChart = ref(null);
+
+// 楗煎浘鍒濆鍖�
+const initPieChart = () => {
+  const chart = echarts.init(pieChart.value);
+  const option = {
+    tooltip: {
+      trigger: "item",
+      formatter: "{a} <br/>{b}: {c} ({d}%)",
+    },
+    legend: {
+      orient: "vertical",
+      left: "right",
+      top: "center",
+      textStyle: {
+        fontSize: 12,
+      },
+    },
+    series: [
+      {
+        name: "钀ユ敹鍒嗗竷",
+        type: "pie",
+        radius: ["30%", "70%"],
+        center: ["40%", "50%"],
+        avoidLabelOverlap: false,
+        label: {
+          show: false,
+          position: "center",
+        },
+        emphasis: {
+          label: {
+            show: true,
+            fontSize: "16",
+            fontWeight: "bold",
+          },
+        },
+        labelLine: {
+          show: false,
+        },
+        data: revenueDistribution.value,
+      },
+    ],
+  };
+  chart.setOption(option);
+  window.addEventListener("resize", () => {
+    chart.resize();
+  });
+};
+
+// 闈㈢Н鍥惧垵濮嬪寲
+const initAreaChart = () => {
+  const chart = echarts.init(areaChart.value);
+  const option = {
+    title: {
+      show: supplyTrend.value.length == 0, // 娌℃暟鎹墠鏄剧ず
+      extStyle: {
+        color: "grey",
+        fontSize: 20,
+      },
+      text: "鏆傛棤鏁版嵁",
+      left: "center",
+      top: "center",
+    },
+    tooltip: {
+      trigger: "axis",
+      axisPointer: {
+        type: "cross",
+        label: {
+          backgroundColor: "#6a7985",
+        },
+      },
+    },
+    legend: {
+      data: ["渚涘簲閲�"],
+      top: 10,
+    },
+    grid: {
+      left: "3%",
+      right: "4%",
+      bottom: "3%",
+      containLabel: true,
+    },
+    xAxis: [
+      {
+        type: "category",
+        boundaryGap: false,
+        data: supplyTrend.value.Xkeys || [],
+        axisLabel: {
+          fontSize: 12,
+        },
+      },
+    ],
+    yAxis: [
+      {
+        type: "value",
+        axisLabel: {
+          fontSize: 12,
+        },
+      },
+    ],
+    series: [
+      {
+        name: "渚涘簲閲�",
+        type: "line",
+        stack: "Total",
+        areaStyle: {
+          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            { offset: 0, color: "rgba(64, 158, 255, 0.3)" },
+            { offset: 1, color: "rgba(64, 158, 255, 0.1)" },
+          ]),
+        },
+        emphasis: {
+          focus: "series",
+        },
+        data: supplyTrend.value.Yvalues || [],
+        lineStyle: {
+          color: "#409EFF",
+        },
+        itemStyle: {
+          color: "#409EFF",
+        },
+      },
+    ],
+  };
+  chart.setOption(option);
+  window.addEventListener("resize", () => {
+    chart.resize();
+  });
+};
+
+// 鏌辩姸鍥惧垵濮嬪寲
+const initBarChart = () => {
+  const chart = echarts.init(barChart.value);
+  const option = {
+    tooltip: {
+      trigger: "axis",
+      axisPointer: {
+        type: "shadow",
+      },
+    },
+    grid: {
+      left: "3%",
+      right: "4%",
+      bottom: "3%",
+      containLabel: true,
+    },
+    xAxis: {
+      type: "category",
+      data: ["鍘熺叅", "绮剧叅", "鐒︾叅", "鍧楃叅", "鐓ゆ偿"],
+      axisLabel: {
+        fontSize: 11,
+      },
+    },
+    yAxis: {
+      type: "value",
+      axisLabel: {
+        fontSize: 11,
+      },
+    },
+    series: [
+      {
+        name: "閿�閲�",
+        type: "bar",
+        data: [320, 302, 301, 334, 290],
+        itemStyle: {
+          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            { offset: 0, color: "#409EFF" },
+            { offset: 1, color: "#79bbff" },
+          ]),
+        },
+        barWidth: "60%",
+      },
+    ],
+  };
+  chart.setOption(option);
+  window.addEventListener("resize", () => {
+    chart.resize();
+  });
+};
+// 鏀跺叆鍒嗗竷鏁版嵁
+const revenueDistribution = ref([]);
+
+// 鍒濆鍖栨墍鏈夊浘琛�
+const initCharts = () => {
+  initPieChart();
+  initAreaChart();
+  initBarChart();
+};
+
+const getList = async () => {
+  try {
+    searchMonth();
+    const res = await getCoalInfo();
+    homePageData.value = res.data || {};
+    revenueDistribution.value = [];
+    if (homePageData.value.revenueDistribution) {
+      Object.keys(homePageData.value.revenueDistribution).forEach((key) => {
+        let obj = {};
+        obj.name = key;
+        obj.value = homePageData.value.revenueDistribution[key];
+        obj.itemStyle = {
+          color: getRandomHexColors(1)[0], // 浣跨敤闅忔満棰滆壊
+        };
+        revenueDistribution.value.push(obj);
+      });
+    }
+    if (homePageData.value.inventory) {
+      let inventoryListXkeys = Object.keys(homePageData.value.inventory);
+      let inventoryListYvalues = Object.values(homePageData.value.inventory);
+      inventoryList.value = {
+        Xkeys: inventoryListXkeys,
+        Yvalues: inventoryListYvalues,
+      };
+    }
+    console.log(inventoryList.value)
+    // 鏁版嵁鍔犺浇瀹屾垚鍚庨噸鏂板垵濮嬪寲鍥捐〃
+    nextTick(() => {
+      initCharts();
+    });
+  } catch (error) {
+    console.error("鑾峰彇鐓ょ淇℃伅澶辫触:", error);
+  }
+};
+const inventoryList = ref([]);
+
+const supplyTrend = ref({});
+const searchMonth = async () => {
+  let res = await getYearlySales({
+    timeRange: selectMonth.value ? selectMonth.value : null,
+  });
+  let Xkeys = Object.keys(res.data.data);
+  let Yvalues = Object.values(res.data.data);
+  supplyTrend.value = {
+    Xkeys,
+    Yvalues,
+  };
+};
+onMounted(() => {
+  getList();
+});
 </script>
 
 <style scoped lang="scss">
+.dashboard {
+  padding: 20px;
+  background-color: #f5f7fa;
+  min-height: 91vh;
+  box-sizing: border-box;
+}
 
+/* 椤堕儴缁熻鍗$墖 */
+.top-cards {
+  display: flex;
+  gap: 20px;
+  margin-bottom: 20px;
+}
+
+.stat-card {
+  flex: 1;
+  background: white;
+  border-radius: 8px;
+  padding: 20px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  display: flex;
+  align-items: center;
+  gap: 15px;
+}
+
+.card-icon {
+  width: 60px;
+  height: 60px;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 24px;
+  color: white;
+}
+
+.revenue .card-icon {
+  background: linear-gradient(135deg, #409eff, #79bbff);
+}
+
+.supply .card-icon {
+  background: linear-gradient(135deg, #67c23a, #95d475);
+}
+
+.card-content {
+  flex: 1;
+}
+
+.card-title {
+  font-size: 14px;
+  color: #909399;
+  margin-bottom: 8px;
+}
+
+.card-value {
+  font-size: 24px;
+  font-weight: bold;
+  color: #303133;
+  margin-bottom: 5px;
+}
+
+.card-trend {
+  font-size: 12px;
+}
+
+.trend-label {
+  color: #909399;
+  margin-right: 5px;
+}
+
+.trend-value.up {
+  color: #67c23a;
+}
+.trend-value.down {
+  color: #f56c6c;
+}
+
+/* 涓棿鍥捐〃鍖哄煙 */
+.chart-section {
+  display: flex;
+  gap: 20px;
+  margin-bottom: 20px;
+}
+.el-scrollbar__view {
+  width: 100%;
+}
+.chart-container {
+  flex: 1;
+  background: white;
+  border-radius: 8px;
+  padding: 20px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.chart-title {
+  font-size: 16px;
+  font-weight: bold;
+  color: #303133;
+  margin-bottom: 15px;
+  padding-bottom: 10px;
+  border-bottom: 2px solid #f0f0f0;
+  display: flex;
+  justify-content: space-between;
+}
+
+.chart-content {
+  height: 280px;
+}
+
+/* 搴曢儴涓夋爮甯冨眬 */
+.bottom-section {
+  display: flex;
+  gap: 20px;
+}
+
+.bottom-card {
+  flex: 1;
+  background: white;
+  border-radius: 8px;
+  padding: 20px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.card-header {
+  margin-bottom: 15px;
+  padding-bottom: 10px;
+  border-bottom: 2px solid #f0f0f0;
+}
+
+.card-header h3 {
+  margin: 0;
+  font-size: 16px;
+  font-weight: bold;
+  color: #303133;
+}
+
+/* 搴撳瓨缁熻鏍峰紡 */
+.inventory-items {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.inventory-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 12px;
+  background: #f8f9fa;
+  border-radius: 6px;
+  border-left: 3px solid #409eff;
+}
+
+.item-name {
+  font-weight: bold;
+  color: #303133;
+}
+
+.item-value {
+  color: #606266;
+  font-size: 14px;
+}
+
+.item-status {
+  padding: 2px 8px;
+  border-radius: 12px;
+  font-size: 12px;
+  font-weight: bold;
+}
+
+.item-status.normal {
+  background: #f0f9ff;
+  color: #67c23a;
+}
+
+.item-status.low {
+  background: #fef0e6;
+  color: #e6a23c;
+}
+
+/* 鏌辩姸鍥惧鍣� */
+.bar-chart {
+  height: 200px;
+}
+
+/* 琛ㄦ牸鏍峰紡璋冩暣 */
+.bottom-card.table {
+  width: 100%;
+}
+
+.bottom-card.table .el-table {
+  font-size: 12px;
+}
+
+.bottom-card.table .el-table td,
+.bottom-card.table .el-table th {
+  padding: 8px 0;
+}
+:deep(.el-scrollbar__view) {
+  width: 100% !important;
+}
+:deep(.el-table__header, ) {
+  width: 100% !important;
+}
+:deep(.el-table__body, ) {
+  width: 100% !important;
+}
+/* 鍝嶅簲寮忚璁� */
+@media (max-width: 1200px) {
+  .bottom-section {
+    flex-direction: column;
+  }
+
+  .chart-section {
+    flex-direction: column;
+  }
+}
+
+@media (max-width: 768px) {
+  .top-cards {
+    flex-direction: column;
+  }
+
+  .dashboard {
+    padding: 10px;
+  }
+
+  .stat-card {
+    padding: 15px;
+  }
+
+  .card-value {
+    font-size: 20px;
+  }
+}
 </style>
-

--
Gitblit v1.9.3