From c586e23d7f75fbfbff0a3114956c721c8b6ebb43 Mon Sep 17 00:00:00 2001 From: gaoluyang <2820782392@qq.com> Date: 星期五, 11 七月 2025 09:31:42 +0800 Subject: [PATCH] 1.库存管理、销售出库-前端分页修改 2.销售出库,必填优化 --- src/views/index.vue | 718 +++++++++++++++++++++++++++++++++++++---------------------- 1 files changed, 454 insertions(+), 264 deletions(-) diff --git a/src/views/index.vue b/src/views/index.vue index 452fcfa..37989a6 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -8,24 +8,50 @@ </div> <div class="card-content"> <div class="card-title">钀ユ敹閲戦</div> - <div class="card-value">楼1,234,567</div> - <div class="card-trend"> + <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">+12.5%</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">8,965 鍚�</div> - <div class="card-trend"> + <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">+8.2%</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> @@ -37,9 +63,25 @@ <div class="chart-title">钀ユ敹鍒嗗竷</div> <div ref="pieChart" class="chart-content pie-chart"></div> </div> - + <div class="chart-container"> - <div class="chart-title">渚涘簲閲忚秼鍔�</div> + <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> @@ -52,25 +94,10 @@ <h3>搴撳瓨缁熻</h3> </div> <div class="inventory-items"> - <div class="inventory-item"> - <div class="item-name">鍘熺叅</div> - <div class="item-value">15,432 鍚�</div> - <div class="item-status normal">姝e父</div> - </div> - <div class="inventory-item"> - <div class="item-name">绮剧叅</div> - <div class="item-value">8,765 鍚�</div> - <div class="item-status normal">姝e父</div> - </div> - <div class="inventory-item"> - <div class="item-name">鐒︾叅</div> - <div class="item-value">3,241 鍚�</div> - <div class="item-status low">鍋忎綆</div> - </div> - <div class="inventory-item"> - <div class="item-name">鍧楃叅</div> - <div class="item-value">6,789 鍚�</div> - <div class="item-status normal">姝e父</div> + <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> @@ -93,243 +120,393 @@ style="width: 100%" :header-cell-style="tableHeaderStyle" > - <el-table-column prop="product" label="浜у搧" width="80"></el-table-column> - <el-table-column prop="quantity" label="鏁伴噺" width="80"></el-table-column> - <el-table-column prop="amount" label="閲戦" width="90"></el-table-column> - <el-table-column prop="status" label="鐘舵��" width="70"> - <template #default="scope"> - <el-tag - :type="scope.row.status === '宸插畬鎴�' ? 'success' : 'warning'" - size="small" - > - {{ scope.row.status }} - </el-tag> - </template> - </el-table-column> + <el-table-column + prop="coalName" + label="浜у搧" + align="center" + mini-width="50" + ></el-table-column> + <el-table-column + prop="inventoryQuantity" + label="鏁伴噺" + align="center" + mini-width="50" + ></el-table-column> + <el-table-column + prop="totalAmount" + label="閲戦" + align="center" + mini-width="50" + ></el-table-column> </el-table> </div> </div> </div> </template> -<script> -import * as echarts from 'echarts' +<!-- 鍒犻櫎澶氫綑鐨� 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"; -export default { - name: 'Dashboard', - data() { - return { - salesData: [ - { product: '鍘熺叅', quantity: '1,234鍚�', amount: '楼456,789', status: '宸插畬鎴�' }, - { product: '绮剧叅', quantity: '567鍚�', amount: '楼234,567', status: '宸插畬鎴�' }, - { product: '鐒︾叅', quantity: '890鍚�', amount: '楼345,678', status: '杩涜涓�' }, - { product: '鍧楃叅', quantity: '432鍚�', amount: '楼123,456', status: '宸插畬鎴�' }, - { product: '鐓ゆ偿', quantity: '678鍚�', amount: '楼234,567', status: '杩涜涓�' } - ], - tableHeaderStyle: { - backgroundColor: '#f5f7fa', - color: '#606266', - fontSize: '12px' - } - } - }, - mounted() { - this.$nextTick(() => { - this.initCharts() - }) - }, - methods: { - initCharts() { - this.initPieChart() - this.initAreaChart() - this.initBarChart() - }, - - initPieChart() { - const chart = echarts.init(this.$refs.pieChart) - 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: [ - { value: 335, name: '鍘熺叅', itemStyle: { color: '#409EFF' } }, - { value: 310, name: '绮剧叅', itemStyle: { color: '#67C23A' } }, - { value: 234, name: '鐒︾叅', itemStyle: { color: '#E6A23C' } }, - { value: 135, name: '鍧楃叅', itemStyle: { color: '#F56C6C' } }, - { value: 155, name: '鍏朵粬', itemStyle: { color: '#909399' } } - ] - } - ] - } - chart.setOption(option) - - // 鍝嶅簲寮� - window.addEventListener('resize', () => { - chart.resize() - }) - }, - - initAreaChart() { - const chart = echarts.init(this.$refs.areaChart) - const option = { - 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: ['1鏈�', '2鏈�', '3鏈�', '4鏈�', '5鏈�', '6鏈�', '7鏈�'], - 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: [1200, 1320, 1010, 1340, 900, 1230, 1100], - lineStyle: { - color: '#409EFF' - }, - itemStyle: { - color: '#409EFF' - } - } - ] - } - chart.setOption(option) - - // 鍝嶅簲寮� - window.addEventListener('resize', () => { - chart.resize() - }) - }, - - initBarChart() { - const chart = echarts.init(this.$refs.barChart) - 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 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 = ref([]); + +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: resultMonthList.value.Xkeys || [], + axisLabel: { + fontSize: 11, + }, + }, + yAxis: { + type: "value", + axisLabel: { + fontSize: 11, + }, + }, + series: [ + { + name: "閿�閲�", + type: "bar", + data: resultMonthList.value.Yvalues || [], + 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, + }; + } + if(homePageData.value.resultMouth){ + let resultMonthXkeys = Object.keys(homePageData.value.resultMouth); + let resultMonthYvalues = Object.values(homePageData.value.resultMouth); + resultMonthList.value = { + Xkeys: resultMonthXkeys, + Yvalues: resultMonthYvalues, + }; + console.log(resultMonthList.value); + } + if(homePageData.value.salesResults){ + salesData.value = homePageData.value.salesResults; + } + // 鏁版嵁鍔犺浇瀹屾垚鍚庨噸鏂板垵濮嬪寲鍥捐〃 + nextTick(() => { + initCharts(); + }); + } catch (error) { + console.error("鑾峰彇鐓ょ淇℃伅澶辫触:", error); + } +}; +const inventoryList = ref([]); +const resultMonthList = 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> +<style scoped lang="scss"> .dashboard { padding: 20px; background-color: #f5f7fa; - min-height: 100vh; + min-height: 91vh; + box-sizing: border-box; } /* 椤堕儴缁熻鍗$墖 */ @@ -362,11 +539,11 @@ } .revenue .card-icon { - background: linear-gradient(135deg, #409EFF, #79bbff); + background: linear-gradient(135deg, #409eff, #79bbff); } .supply .card-icon { - background: linear-gradient(135deg, #67C23A, #95d475); + background: linear-gradient(135deg, #67c23a, #95d475); } .card-content { @@ -396,7 +573,10 @@ } .trend-value.up { - color: #67C23A; + color: #67c23a; +} +.trend-value.down { + color: #f56c6c; } /* 涓棿鍥捐〃鍖哄煙 */ @@ -405,7 +585,7 @@ gap: 20px; margin-bottom: 20px; } -.el-scrollbar__view{ +.el-scrollbar__view { width: 100%; } .chart-container { @@ -423,6 +603,8 @@ margin-bottom: 15px; padding-bottom: 10px; border-bottom: 2px solid #f0f0f0; + display: flex; + justify-content: space-between; } .chart-content { @@ -470,7 +652,7 @@ padding: 12px; background: #f8f9fa; border-radius: 6px; - border-left: 3px solid #409EFF; + border-left: 3px solid #409eff; } .item-name { @@ -492,12 +674,12 @@ .item-status.normal { background: #f0f9ff; - color: #67C23A; + color: #67c23a; } .item-status.low { background: #fef0e6; - color: #E6A23C; + color: #e6a23c; } /* 鏌辩姸鍥惧鍣� */ @@ -507,7 +689,7 @@ /* 琛ㄦ牸鏍峰紡璋冩暣 */ .bottom-card.table { - min-width: 320px; + width: 100%; } .bottom-card.table .el-table { @@ -518,13 +700,21 @@ .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; } @@ -534,15 +724,15 @@ .top-cards { flex-direction: column; } - + .dashboard { padding: 10px; } - + .stat-card { padding: 15px; } - + .card-value { font-size: 20px; } -- Gitblit v1.9.3