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