From cb78cb10939dc95b55e26b68cd805f2717ea48f0 Mon Sep 17 00:00:00 2001 From: gaoluyang <2820782392@qq.com> Date: 星期一, 28 七月 2025 17:55:13 +0800 Subject: [PATCH] 1.首页联调 --- src/views/index.vue | 1436 +++++++++++++++++++++++++++++++------------------------ src/components/Echarts/echarts.vue | 40 src/store/modules/user.js | 12 src/api/viewIndex.js | 47 - 4 files changed, 868 insertions(+), 667 deletions(-) diff --git a/src/api/viewIndex.js b/src/api/viewIndex.js index 23bddd8..2d85171 100644 --- a/src/api/viewIndex.js +++ b/src/api/viewIndex.js @@ -1,48 +1,43 @@ // 棣栭〉鎺ュ彛 import request from '@/utils/request' -// 鏈湀鍚堝悓閲戦 -export const getContractAmount = () => { +// 閿�鍞�-閲囪喘-搴撳瓨鏁版嵁 +export const getBusiness = () => { return request({ - url: '/sales/ledger/getContractAmount', + url: '/home/business', method: 'get' }) } -// 鏈湀寮�绁ㄩ噾棰� -export const getInvoiceAmount = () => { +// 瀹㈡埛鍚堝悓閲戦鍒嗘瀽 +export const analysisCustomerContractAmounts = () => { return request({ - url: '/invoiceLedger/getInvoiceAmount', + url: '/home/analysisCustomerContractAmounts', method: 'get' }) } -// 鏈湀鍥炴閲戦 -export const getReceiptAmount = () => { +// 璐ㄦ鍒嗘瀽 +export const qualityStatistics = () => { return request({ - url: '/receiptPayment/getReceiptAmount', + url: '/home/qualityStatistics', method: 'get' }) } -// 瀹㈡埛鍚堝悓閲戦TOP5缁熻 -export const getTopFiveList = () => { +// 搴旀敹搴斾粯缁熻 +export const statisticsReceivablePayable = (query) => { return request({ - url: '/sales/ledger/getTopFiveList', + url: '/home/statisticsReceivablePayable', + method: 'get', + params: query + }) +} +// 寰呭姙浜嬮」 +export const homeTodos = () => { + return request({ + url: '/home/todos', method: 'get' }) } -// 鍥炴楗肩姸鍥� -export const getAmountMouth = () => { - return request({ - url: '/receiptPayment/getAmountMouth', - method: 'get' - }) -} -// 浠樻楗肩姸鍥� -export const paymentMonthList = () => { - return request({ - url: '/purchase/paymentRegistration/paymentMonthList', - method: 'get' - }) -} + // 绾垮舰鍥� export const getAmountHalfYear = () => { return request({ diff --git a/src/components/Echarts/echarts.vue b/src/components/Echarts/echarts.vue index 4601e41..2c51ace 100644 --- a/src/components/Echarts/echarts.vue +++ b/src/components/Echarts/echarts.vue @@ -70,7 +70,15 @@ maskColor: 'rgba(255, 255, 255, 0.8)', zlevel: 0 }) - } + }, + color: { + type: Array, + default: () => [] + }, + option: { + type: Object, + default: () => ({}) + }, }) import { watch } from 'vue' @@ -83,28 +91,26 @@ function generateChart(option) { const copiedOption = JSON.parse(JSON.stringify(option)) // 鉁� 娣辨嫹璐� - // if (copiedOption.series && copiedOption.series.length > 0) { - // copiedOption.series.forEach((s, index) => { - // if (s.type === 'line') { - // s.itemStyle = { - // color: props.lineColors[index] || props.lineColors[0] - // } - // s.lineStyle = { - // color: props.lineColors[index] || props.lineColors[0] - // } - // } else if (s.type === 'bar') { - // s.itemStyle = { - // color: props.barColors[index] || props.barColors[0] - // } - // } - // }) - // } + if (copiedOption.series && copiedOption.series.length > 0) { + copiedOption.series.forEach((s, index) => { + if (s.type === 'line' && props.lineColors.length) { + s.itemStyle = s.itemStyle || {} + s.lineStyle = s.lineStyle || {} + s.itemStyle.color = props.lineColors[index] || props.lineColors[0] + s.lineStyle.color = props.lineColors[index] || props.lineColors[0] + } else if (s.type === 'bar' && props.barColors.length) { + s.itemStyle = s.itemStyle || {} + s.itemStyle.color = props.barColors[index] || props.barColors[0] + } + }) + } chartInstance.setOption(copiedOption) } function renderChart() { const option = { + color: props.color.length ? props.color : undefined, backgroundColor: props.options.backgroundColor || '#fff', xAxis: props.xAxis, yAxis: props.yAxis, diff --git a/src/store/modules/user.js b/src/store/modules/user.js index 7c392fc..a007574 100644 --- a/src/store/modules/user.js +++ b/src/store/modules/user.js @@ -31,6 +31,16 @@ }) }) }, + getCurrentTime() { + const now = new Date(); + const year = now.getFullYear(); // 鑾峰彇骞翠唤 + const month = String(now.getMonth() + 1).padStart(2, '0'); // 鏈堜唤浠�0寮�濮嬶紝瑕�+1锛屽苟琛ラ浂 + const day = String(now.getDate()).padStart(2, '0'); // 鏃ユ湡琛ラ浂 + const hours = String(now.getHours()).padStart(2, '0'); // 灏忔椂琛ラ浂 + const minutes = String(now.getMinutes()).padStart(2, '0'); // 鍒嗛挓琛ラ浂 + const seconds = String(now.getSeconds()).padStart(2, '0'); // 绉掓暟琛ラ浂 + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + }, // 鑾峰彇鐢ㄦ埛淇℃伅 getInfo() { return new Promise((resolve, reject) => { @@ -49,7 +59,9 @@ this.avatar = avatar this.currentFactoryName = user.currentFactoryName this.nickName = user.nickName + this.roleName = user.roles[0].roleName this.currentDeptId = user.tenantId + this.currentLoginTime = this.getCurrentTime() resolve(res) }).catch(error => { reject(error) diff --git a/src/views/index.vue b/src/views/index.vue index f6ae965..163366a 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -1,624 +1,812 @@ -<template> - <div class="app-container home"> - <div style="display: flex;"> - <div> - <div class="card-top-left"> - <div class="title"> - <span style="font-weight: bold">鏈湀閿�鍞�侀噰璐儏鍐佃鍒�</span> - </div> - <div class="card-group"> - <div class="info-card"> - <div class="info-message"> - <div class="info-number">{{ contractAmount }}</div> - <div class="info-title">鍚堝悓閲戦(鍏�)</div> - </div> - <img src="@/assets/images/icon1.png" alt="" style="width: 63px;height: 63px"> - </div> - <div class="info-card1"> - <div class="info-message"> - <div class="info-number">{{ invoiceAmount }}</div> - <div class="info-title">寮�绁ㄩ噾棰�(鍏�)</div> - </div> - <img src="@/assets/images/icon2.png" alt="" style="width: 63px;height: 63px"> - </div> - <div class="info-card2"> - <div class="info-message"> - <div class="info-number">{{ receiptAmount }}</div> - <div class="info-title">鍥炴閲戦(鍏�)</div> - </div> - <img src="@/assets/images/icon%203.png" alt="" style="width: 63px;height: 63px"> - </div> - </div> - </div> - <div class="card-top-left"> - <div class="title"> - <span style="font-weight: bold">鏈湀搴旀敹銆佸簲浠樻儏鍐佃鍒�</span> - </div> - <div class="pie"> - <div class="card-group"> - <div class="pie-group"> - <div style="margin-right: 80px"> - <Echarts ref="chart" :legend="pieLegend" :chartStyle="chartStyle" :series="materialPieSeries" - :tooltip="pieTooltip"></Echarts> - </div> - <div class="info-message2"> - <div class="info-message1"> - <div class="pie-title">鏈湀鍥炴閲戦</div> - <div class="pie-info"><span class="pie-number">{{ receiveAmount }}</span>鍏� <span - class="pie-number">{{ receiveAmountPercentage }}</span>%</div> - </div> - <div class="info-message1"> - <div class="pie-title">搴旀敹娆鹃噾棰�</div> - <div class="pie-info"><span class="pie-number">{{ contractAmountMonth }}</span>鍏�</div> - </div> - </div> - </div> - </div> - <div class="card-group"> - <div class="pie-group"> - <div style="margin-right: 80px"> - <Echarts ref="chart" :options="options" :legend="pieLegend" :chartStyle="chartStyle" - :series="materialPieSeries1" :tooltip="pieTooltip1"></Echarts> - </div> - <div class="info-message2"> - <div class="info-message1"> - <div class="pie-title1">鏈湀浠樻閲戦</div> - <div class="pie-info"><span class="pie-number1">{{ paymentAmount }}</span>鍏� <span - class="pie-number1">{{ payableAmountPercentage }}</span>%</div> - </div> - <div class="info-message1"> - <div class="pie-title1">搴斾粯娆鹃噾棰�</div> - <div class="pie-info"><span class="pie-number1">{{ payableAmount }}</span>鍏�</div> - </div> - </div> - </div> - </div> - </div> - </div> - </div> - <div> - <div class="card-top-right"> - <div class="title"> - <span style="font-weight: bold">瀹㈡埛鍚堝悓閲戦TOP5缁熻</span> - </div> - <div> - <Echarts ref="chart" :chartStyle="chartStyle1" :grid="grid" :legend="barLegend" :series="barSeries" - :tooltip="tooltip" :xAxis="xAxis1" :yAxis="yAxis1" style="height: 42vh;"></Echarts> - </div> - </div> - </div> - </div> - <div> - <div> - <div class="card-bottom"> - <div class="title"> - <span style="font-weight: bold">鍥炴銆佸紑绁ㄨ繎鍗婂勾璧板娍鍥�</span> - </div> - <div> - <Echarts ref="chart" :chartStyle="chartStyle1" :grid="grid" :legend="barLegend" :series="lineSeries" - :tooltip="tooltipLine" :xAxis="xAxis2" :yAxis="yAxis2" style="height: 27vh;"></Echarts> - </div> - </div> - </div> - </div> - </div> -</template> - -<script setup name="Index"> -const { proxy } = getCurrentInstance() -import * as echarts from 'echarts'; -import Echarts from "@/components/Echarts/echarts.vue"; -import { - getAmountHalfYear, - getAmountMouth, - getContractAmount, - getInvoiceAmount, - getReceiptAmount, - getTopFiveList, paymentMonthList -} from "@/api/viewIndex.js"; - -const pieLegend = reactive({ - show: false, -}) -const contractAmount = ref(0) -const invoiceAmount = ref(0) -const receiptAmount = ref(0) -const receiveAmount = ref(0) -const contractAmountMonth = ref(0) -const receiveAmountPercentage = ref(0) -const paymentAmount = ref(0) -const payableAmount = ref(0) -const payableAmountPercentage = ref(0) -const options = reactive({ - graphic: { - type: 'circle', - left: 'center', - top: 'middle', - shape: { - r: '70%' // 鍦嗗舰鍗婂緞涓庨ゼ鍥惧鍦堢浉鍚� - }, - } -}) -const pieTooltip = reactive({ - trigger: 'item', - formatter: function (params) { - // 鍔ㄦ�佺敓鎴愭彁绀轰俊鎭紝鍩轰簬鏁版嵁椤圭殑 name 灞炴�� - const description = params.name === '鏈湀鍥炴閲戦' ? '鏈湀鍥炴閲戦' : '搴旀敹娆鹃噾棰�'; - return `${description} ${formatNumber(params.value)}鍏� ${params.percent}%`; - }, - position: 'right' -}) -const pieTooltip1 = reactive({ - trigger: 'item', - formatter: function (params) { - // 鍔ㄦ�佺敓鎴愭彁绀轰俊鎭紝鍩轰簬鏁版嵁椤圭殑 name 灞炴�� - const description = params.name === '鏈湀浠樻閲戦' ? '鏈湀浠樻閲戦' : '搴斾粯娆鹃噾棰�'; - return `${description} ${formatNumber(params.value)}鍏� ${params.percent}%`; - }, - position: 'right' -}) -// 鏁板瓧鏍煎紡鍖栧嚱鏁帮紝娣诲姞閫楀彿浣滀负鍗冧綅鍒嗛殧绗� -function formatNumber(num) { - return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); -} -const materialPieSeries = ref([ - { - type: 'pie', - radius: '92%', - avoidLabelOverlap: false, - itemStyle: { - borderColor: '#fff', - borderWidth: 2 - }, - label: { - show: false - }, - data: [ - { value: 0, name: '鏈湀鍥炴閲戦', itemStyle: { color: '#2D99FF' } }, - { value: 0, name: '搴旀敹娆鹃噾棰�', itemStyle: { color: '#D4DDFF' } }, - ] - } -]) -const materialPieSeries1 = ref([ - { - type: 'pie', - radius: '92%', - avoidLabelOverlap: false, - itemStyle: { - borderColor: '#fff', - borderWidth: 2 - }, - label: { - show: false - }, - data: [ - { value: 0, name: '鏈湀浠樻閲戦', itemStyle: { color: '#1EBFAC' } }, - { value: 0, name: '搴斾粯娆鹃噾棰�', itemStyle: { color: '#D0EFE1' } }, - ] - } -]) -const chartStyle = { - width: '150%', - height: '120%' // 璁剧疆鍥捐〃瀹瑰櫒鐨勯珮搴� -} -const chartStyle1 = { - width: '100%', - height: '100%' // 璁剧疆鍥捐〃瀹瑰櫒鐨勯珮搴� -} -const grid = { - left: '2%', // 澧炲ぇ宸︿晶鐣欑櫧 - right: '10%', - bottom: '15%', - top: '10%', - containLabel: true // 纭繚鍖呭惈鏍囩 -} -const tooltip = { - trigger: 'axis', - axisPointer: { - type: 'shadow' - } -} -const tooltipLine = { - trigger: 'axis', -} -const yAxis1 = ref([ - { - type: 'value', - } -]) -const xAxis1 = ref([ - { - type: 'category', - data: [] - } -]) -const yAxis2 = ref([ - { - type: 'value', - } -]) -const xAxis2 = ref([ - { - type: 'category', - data: [] - } -]) -const barLegend = reactive({}) -const barSeries = ref([ - { - type: 'bar', - data: [], - label: { - show: true - }, - }, -]) -const lineSeries = ref([ - { - type: 'line', - data: [], - label: { - show: true - }, - }, -]) -// 鍚堝悓閲戦 -const getContractAmountNum = () => { - getContractAmount().then((res) => { - contractAmount.value = res.data - }) -} -// 寮�绁ㄩ噾棰� -const getInvoiceAmountNum = () => { - getInvoiceAmount().then((res) => { - invoiceAmount.value = res.data - }) -} -// 鍥炴閲戦 -const getReceiptAmountNum = () => { - getReceiptAmount().then((res) => { - receiptAmount.value = res.data - }) -} -// 鏈堝洖娆鹃噾棰濋ゼ鍥� -const getAmountMouthNum = () => { - getAmountMouth().then((res) => { - receiveAmount.value = res.data.receiveAmount - contractAmountMonth.value = res.data.contractAmount - const percentage = (receiveAmount.value / contractAmountMonth.value) * 100; - receiveAmountPercentage.value = percentage.toFixed(2) - materialPieSeries.value[0].data[0].value = receiveAmount.value - materialPieSeries.value[0].data[1].value = contractAmountMonth.value - }) -} -// 鏈堜粯娆鹃噾棰濋ゼ鍥� -const paymentMonthListNum = () => { - paymentMonthList().then((res) => { - paymentAmount.value = res.data.paymentAmount - payableAmount.value = res.data.payableAmount - const percentage = (paymentAmount.value / payableAmount.value) * 100; - payableAmountPercentage.value = percentage.toFixed(2) - materialPieSeries1.value[0].data[0].value = paymentAmount.value - materialPieSeries1.value[0].data[1].value = payableAmount.value - }) -} -// 瀹㈡埛top5 -const getTopFiveListNum = async () => { - const res = await getTopFiveList() - const customerName = [] - const totalAmount = [] - res.data.forEach(item => { - customerName.push(item.customerName) - totalAmount.push(item.totalAmount) - }) - // 姝g‘鍝嶅簲寮忚祴鍊硷細鍒涘缓鏂扮殑 xAxis 鍜� series 瀵硅薄 - xAxis1.value = [ - { - type: 'category', - data: customerName - } - ] - barSeries.value = [ - { - type: 'bar', - data: totalAmount, - itemStyle: { - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { offset: 0, color: '#F7D2FF' }, - { offset: 1, color: '#826AF9' } - ]) - }, - emphasis: { - itemStyle: { - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { offset: 1, color: '#826AF9' } - ]) - } - }, - } - ] -} -// 绾垮舰鍥� -const getAmountHalfYearNum = async () => { - const res = await getAmountHalfYear() - console.log(res) - const monthName = [] - const receiptAmount = [] - const invoiceAmount = [] - res.data.forEach(item => { - monthName.push(item.month) - receiptAmount.push(item.receiptAmount) - invoiceAmount.push(item.invoiceAmount) - }) - // 姝g‘鍝嶅簲寮忚祴鍊硷細鍒涘缓鏂扮殑 xAxis 鍜� series 瀵硅薄 - xAxis2.value = [ - { - type: 'category', - data: monthName - } - ] - lineSeries.value = [ - { - name: '寮�绁�', - type: 'line', - data: receiptAmount, - smooth: true, - stack: 'Total', - areaStyle: { - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { - offset: 0, - color: 'rgba(131, 207, 255, 1)' - }, - { - offset: 1, - color: 'rgba(186, 228, 255, 1)' - } - ]) - }, - // 璁剧疆灏忓渾鐐圭殑棰滆壊 - itemStyle: { - color: '#2D99FF', // 灏忓渾鐐归鑹茶缃负#2D99FF - borderColor: '#2D99FF' // 濡傛灉闇�瑕佺殑璇濓紝鍙互璁剧疆杈规棰滆壊 - }, - emphasis: { - focus: 'series' - }, - lineStyle: { - width: 0 - }, - showSymbol: false, - }, - { - name: '鍥炴', - type: 'line', - data: invoiceAmount, - smooth: true, - stack: 'Total', - lineStyle: { - width: 0 - }, - // 璁剧疆灏忓渾鐐圭殑棰滆壊 - itemStyle: { - color: '#83CFFF', // 灏忓渾鐐归鑹茶缃负#83CFFF - borderColor: '#83CFFF' // 濡傛灉闇�瑕佺殑璇濓紝鍙互璁剧疆杈规棰滆壊 - }, - showSymbol: false, - areaStyle: { - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { - offset: 0, - color: 'rgba(54, 153, 255, 1)' - }, - { - offset: 1, - color: 'rgba(89, 169, 254, 1)' - } - ]) - }, - emphasis: { - focus: 'series' - }, - } - ] -} -getContractAmountNum() -getInvoiceAmountNum() -getReceiptAmountNum() -getTopFiveListNum() -getAmountMouthNum() -paymentMonthListNum() -getAmountHalfYearNum() -</script> - -<style scoped> -.card-top-left { - padding: 16px; - background: #fff; - height: 24vh; - width: 56vw; - margin-bottom: 20px; -} - -.card-top-right { - padding: 16px; - background: #fff; - height: 50.6vh; - width: 28vw; - margin-bottom: 20px; - margin-left: 20px; -} - -.card-bottom { - padding: 16px; - background: #fff; - height: 34vh; - width: 85.2vw; - margin-bottom: 20px; -} - -.title { - position: relative; - font-size: 18px; - color: #333; - font-weight: 400; - padding-left: 10px; - margin-bottom: 26px; -} - -.title::before { - position: absolute; - left: 0; - top: 4px; - content: ''; - width: 4px; - height: 18px; - background-color: #002FA7; - border-radius: 2px; -} - -.card-group { - display: flex; -} - -.info-card { - width: 300px; - height: 126px; - background-image: url("../assets/images/Rectangle 76@2x.png"); - background-size: 100% 100%; - display: flex; - justify-content: space-around; - align-items: center; -} - -.info-card1 { - width: 300px; - height: 126px; - background-image: url("../assets/images/Rectangle 77@2x.png"); - background-size: 100% 100%; - margin: 0 40px; - display: flex; - justify-content: space-around; - align-items: center; -} - -.info-card2 { - width: 300px; - height: 126px; - background-image: url("../assets/images/Rectangle 77@2x(1).png"); - background-size: 100% 100%; - display: flex; - justify-content: space-around; - align-items: center; -} - -.info-message { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} - -.info-message1 { - font-weight: bold; - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; -} - -.info-message2 { - font-weight: bold; - display: flex; - flex-direction: column; - align-items: center; - justify-content: space-around; -} - -.info-number { - font-weight: bold; - font-size: 32px; - color: #FFFFFF; - margin-bottom: 10px; -} - -.info-title { - font-weight: bold; - font-size: 18px; - color: #FFFFFF; -} - -.pie { - display: flex; - flex-direction: row; - justify-content: space-around; -} - -.pie-group { - display: flex; -} - -.pie-title { - font-size: 14px; - line-height: 24px; - color: #2853FD; - padding-left: 16px; - position: relative; -} - -.pie-title::before { - content: ''; - width: 6px; - /* 钃濈偣鐨勫搴� */ - height: 6px; - /* 钃濈偣鐨勯珮搴� */ - background-color: #2853FD; - /* 钃濈偣鐨勯鑹� */ - border-radius: 50%; - /* 灏嗘鏂瑰舰鍙樹负鍦嗗舰 */ - position: absolute; - left: 0; - /* 瀹氫綅鍒板乏杈� */ - top: 9px; - /* 鍨傜洿灞呬腑瀵归綈锛屾牴鎹楂樿皟鏁� */ -} - -.pie-title1 { - font-size: 14px; - line-height: 24px; - color: #1EBFAC; - padding-left: 16px; - position: relative; -} - -.pie-title1::before { - content: ''; - width: 6px; - /* 钃濈偣鐨勫搴� */ - height: 6px; - /* 钃濈偣鐨勯珮搴� */ - background-color: #1EBFAC; - /* 钃濈偣鐨勯鑹� */ - border-radius: 50%; - /* 灏嗘鏂瑰舰鍙樹负鍦嗗舰 */ - position: absolute; - left: 0; - /* 瀹氫綅鍒板乏杈� */ - top: 9px; - /* 鍨傜洿灞呬腑瀵归綈锛屾牴鎹楂樿皟鏁� */ -} - -.pie-info { - padding-left: 16px; - font-size: 14px; - line-height: 24px; -} - -.pie-number { - color: #2853FD; -} - -.pie-number1 { - color: #1EBFAC; -} -</style> +<template> + <div class="dashboard"> + <!-- 椤堕儴妯悜涓ゆ爮 --> + <div class="dashboard-top"> + <!-- 宸︼細浼佷笟淇℃伅+涓夊ぇ鏁版嵁鍗$墖锛堜笂涓嬫帓鍒楋級 --> + <div class="top-left"> + <div class="company-info"> + <div class="section-title">鐧婚檰淇℃伅</div> + <div style="display: flex;align-items: center;gap: 20px"> + <img :src="userStore.avatar" class="avatar" alt=""/> + <div class="company-card"> + <div class="company-name">{{userStore.name}}</div> + <div class="company-meta">{{userStore.roleName}}</div> + </div> + <div style="display: flex;align-items: center;gap: 8px"> + <el-icon color="#5053B5" size="22"><Clock /></el-icon> + <span>鐧婚檰鏃ユ湡锛歿{userStore.currentLoginTime}}</span> + </div> + </div> + </div> + <div class="data-cards"> + <div class="data-card sales"> + <div class="data-title">閿�鍞暟鎹�</div> + <div class="data-num"> + <div> + <div class="data-desc">鏈湀閿�鍞/鍏�</div> + <div class="data-value">{{businessInfo.monthSaleMoney}}</div> + </div> + <div> + <div class="data-desc">鏈紑绁ㄩ噾棰�/鍏�</div> + <div class="data-value">{{businessInfo.monthSaleHaveMoney}}</div> + </div> + </div> + + </div> + <div class="data-card purchase"> + <div class="data-title">閲囪喘鏁版嵁</div> + <div class="data-num"> + <div> + <div class="data-desc">鏈湀閲囪喘棰�/鍏�</div> + <div class="data-value">{{businessInfo.monthPurchaseMoney}}</div> + </div> + <div> + <div class="data-desc">寰呬粯娆鹃噾棰�/鍏�</div> + <div class="data-value">{{businessInfo.monthPurchaseHaveMoney}}</div> + </div> + </div> + </div> + <div class="data-card inventory"> + <div class="data-title">搴撳瓨鏁版嵁</div> + <div class="data-num"> + <div> + <div class="data-desc">褰撳墠搴撳瓨鎬婚噺/浠�</div> + <div class="data-value">{{businessInfo.inventoryNum}}</div> + </div> + <div> + <div class="data-desc">浠婃棩鍏ュ簱/浠�</div> + <div class="data-value">{{businessInfo.todayInventoryNum}}</div> + </div> + </div> + </div> + </div> + </div> + <!-- 鍙筹細寰呭姙浜嬮」 --> + <div class="todo-panel"> + <div class="section-title">寰呭姙浜嬮」</div> + <ul class="todo-list" v-if="todoList.length > 0"> + <li v-for="item in todoList" :key="item.id"> + <div style="display: flex;flex-direction: column;justify-content: space-between;width: 100%;gap: 20px"> + <div style="display: flex;justify-content: space-between;align-items: center;"> + <div class="todo-title">娴佺▼缂栧彿锛歿{item.approveId}}</div> + <div class="todo-division">鐢宠閮ㄩ棬锛歿{item.approveDeptName}}</div> + <div class="todo-time">{{item.approveTime}}</div> + </div> + <div class="todo-division">瀹℃壒浜嬬敱锛歿{item.approveReason}}</div> + </div> + </li> + </ul> + <div v-else style="text-align: center"> + 鏆傛棤鏁版嵁 + </div> + </div> + </div> + + <!-- 涓儴妯悜涓ゆ爮 --> + <div class="dashboard-row"> + <div class="main-panel"> + <div class="section-title">瀹㈡埛鍚堝悓閲戦鍒嗘瀽</div> + <div class="contract-summary"> + <div class="contract-info"> + <img src="../assets/images/khtitle.png" alt="" style="width: 42px"/> + <div class="contract-card"> + <div class="contract-name">鎬诲悎鍚岄噾棰�(鍏�)</div> + <div class="contract-meta"> + <div class="main-amount">{{sum}}</div> + <div>鍛ㄥ悓姣�: <span class="up">{{yny}}% </span> 鏃ョ幆姣�: <span class="up">{{chain}}% </span></div> + </div> + </div> + </div> + </div> + <div style="display: flex;align-items: center;gap: 20px;justify-content: space-evenly;height: 180px;margin-top: 20px"> + <div> + <Echarts ref="chart" :legend="pieLegend" :chartStyle="chartStylePie" + :series="materialPieSeries" + :tooltip="pieTooltip"></Echarts> + </div> + <ul class="contract-list"> + <li v-for="item in materialPieSeries[0].data" :key="item.name"> + <div style="display: flex;align-items: center;justify-content: space-between;width: 100%"> + <div class="line" :style="{color: item.itemStyle.color}">鈼弡{item.name}}</div> + <div style="width: 70px">{{item.rate}}%</div> + <div>锟{item.value}}</div> + </div> + </li> + </ul> + </div> + </div> + <div class="main-panel"> + <div style="display: flex;justify-content: space-between;"> + <div class="section-title">搴旀敹搴斾粯缁熻</div> + <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable"> + <el-radio-button label="鎸夊懆" :value="1" /> + <el-radio-button label="鎸夋湀" :value="2" /> + <el-radio-button label="鎸夊搴�" :value="3" /> + </el-radio-group> + </div> + <Echarts ref="chart" + :color="barColors2" + :chartStyle="chartStyle" + :grid="grid" + :series="barSeries" + :tooltip="tooltip" + :xAxis="xAxis" + :yAxis="yAxis" + style="height: 260px"></Echarts> + </div> + </div> + + <!-- 搴曢儴妯悜涓ゆ爮 --> + <div class="dashboard-row"> + <div class="main-panel"> + <div class="section-title">璐ㄩ噺缁熻</div> + <div class="quality-cards"> + <div class="quality-card one">鍘熸潗鏂欏凡妫�娴嬫暟 <span>{{qualityStatisticsObject.supplierNum}}浠�</span></div> + <div class="quality-card two">杩囩▼妫�楠屾暟閲� <span>{{qualityStatisticsObject.processNum}}浠�</span></div> + <div class="quality-card three">鍑哄巶宸叉鏁伴噺 <span>{{qualityStatisticsObject.factoryNum}}浠�</span></div> + </div> + <Echarts ref="chart" + :chartStyle="chartStyle" + :grid="grid" + :legend="barLegend" + :series="barSeries1" + :tooltip="tooltip" + :xAxis="xAxis1" + :yAxis="yAxis1" + style="height: 260px"></Echarts> + </div> + <div class="main-panel"> + <div class="section-title">鍥炴涓庡紑绁ㄥ垎鏋�</div> + <Echarts ref="chart" :chartStyle="chartStyle" :grid="grid" :legend="lineLegend" :series="lineSeries" + :tooltip="tooltipLine" :xAxis="xAxis2" :yAxis="yAxis2" style="height: 270px;"></Echarts> + </div> + </div> + </div> +</template> + +<script setup> +import { ref, onMounted } from 'vue' +import Echarts from "@/components/Echarts/echarts.vue"; +import * as echarts from 'echarts'; +import useUserStore from "@/store/modules/user.js"; +import { + analysisCustomerContractAmounts, getAmountHalfYear, + getBusiness, + homeTodos, + qualityStatistics, + statisticsReceivablePayable +} from "@/api/viewIndex.js"; + +const userStore = useUserStore() + +const businessInfo = ref({ + inventoryNum: 0, + monthPurchaseHaveMoney: 0, + monthPurchaseMoney: 0, + monthSaleHaveMoney: 0, + monthSaleMoney: 0, + todayInventoryNum: 0, +}) +const qualityStatisticsObject = ref({ + supplierNum: 0, + processNum: 0, + factoryNum: 0, +}) +const sum = ref(0) +const yny = ref(0) +const chain = ref(0) + +const pieLegend = reactive({ + show: false, +}) +const barSeries = ref([ + { + type: 'bar', + data: [], + label: { + show: true, + } + }, +]) +const barSeries1 = ref([ + { + name: '鍘熸潗鏂欎笉鍚堟牸鏁�', + type: 'bar', + barGap: 0, + emphasis: { + focus: 'series' + }, + data: [] + }, + { + name: '杩囩▼涓嶅悎鏍兼暟', + type: 'bar', + emphasis: { + focus: 'series' + }, + data: [] + }, + { + name: '鍑哄巶涓嶅悎鏍兼暟', + type: 'bar', + emphasis: { + focus: 'series' + }, + data: [] + }, +]) +const chartStyle = { + width: '100%', + height: '100%' // 璁剧疆鍥捐〃瀹瑰櫒鐨勯珮搴� +} +const chartStylePie = { + width: '140%', + height: '140%' // 璁剧疆鍥捐〃瀹瑰櫒鐨勯珮搴� +} +const grid = { + left: '3%', + right: '4%', + bottom: '3%', + containLabel: true +} +const barLegend = { + show: true, + data: ['鍘熸潗鏂欎笉鍚堟牸鏁�', '杩囩▼涓嶅悎鏍兼暟', '鍑哄巶涓嶅悎鏍兼暟'] +} +const barLegend1 = { + show: true, + data: ['棰勪粯璐︽', '搴斾粯璐︽', '棰勬敹璐︽', '搴旀敹璐︽'] +} +const lineLegend = { + show: true, + data: ['寮�绁�', '鍥炴'] +} +const tooltip = { + trigger: 'axis', + axisPointer: { + type: 'shadow' + } +} +const xAxis = [{ + type: 'value', +}] +const xAxis1 = ref([{ + type: 'category', + axisTick: { show: false }, + data: [] +}]) +const yAxis = [{ + type: 'category', + data: ['棰勪粯璐︽', '搴斾粯璐︽', '棰勬敹璐︽', '搴旀敹璐︽',] +}] +const yAxis1 = [{ + type: 'value' +}] +const pieTooltip = reactive({ + trigger: 'item', + formatter: function (params) { + // 鍔ㄦ�佺敓鎴愭彁绀轰俊鎭紝鍩轰簬鏁版嵁椤圭殑 name 灞炴�� + const description = params.name === '鏈湀鍥炴閲戦' ? '鏈湀鍥炴閲戦' : '搴旀敹娆鹃噾棰�'; + return `${description} ${formatNumber(params.value)}鍏� ${params.percent}%`; + }, + position: 'right' +}) +const materialPieSeries = ref([ + { + type: 'pie', + radius: ['66%', '90%'], + avoidLabelOverlap: false, + itemStyle: { + borderColor: '#fff', + borderWidth: 2 + }, + label: { + show: false + }, + data: [] + } +]) +const lineSeries = ref([ + { + type: 'line', + data: [], + label: { + show: true + }, + showSymbol: true, // 鏄剧ず鍦嗙偣 + }, +]) +const tooltipLine = { + trigger: 'axis', +} +const yAxis2 = ref([ + { + type: 'value', + } +]) +const xAxis2 = ref([ + { + type: 'category', + data: [], + axisLabel: { + interval: 0, + formatter: function(value) { + return value.replace(/~/g, '\n'); + }, + } + } +]) + +// 鍚堝悓閲戦鍒嗘瀽鏁版嵁 +const contractList = [ + { name: '娣卞湷绉戞妧鏈夐檺鍏徃', percent: 36, value: 4544, color: '#4fc3f7' }, + { name: '鍖椾含绉戞妧鏈夐檺鍏徃', percent: 20, value: 4000, color: '#81c784' }, + { name: '骞垮窞绉戞妧鏈夐檺鍏徃', percent: 16, value: 3113, color: '#ffb74d' }, + { name: '涓婃捣绉戞妧鏈夐檺鍏徃', percent: 10, value: 2341, color: '#ba68c8' }, + { name: '鍗椾含绉戞妧鏈夐檺鍏徃', percent: 9, value: 1231, color: '#e57373' }, + { name: '鍖椾含鏈潵绉戞妧鏈夐檺鍏徃', percent: 9, value: 1231, color: '#64b5f6' } +] + +// 寰呭姙浜嬮」 +const todoList = ref([]) +const radio1 = ref(1) + +// 鍥捐〃寮曠敤 +const barChart = ref(null) +const lineChart = ref(null) +const barColors2 = ['#5181DB', '#D369E0', '#F2CA6D', '#60CCA8'] + +// 闅忔満棰滆壊鐢熸垚鍑芥暟 +const getRandomColor = () => { + return '#' + Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0'); +} + +onMounted(() => { + getBusinessData() + analysisCustomer() + todoInfoS() + statisticsReceivable() + qualityStatisticsInfo() + getAmountHalfYearNum() +}) +// 鏁版嵁缁熻 +const getBusinessData = () => { + getBusiness().then((res) => { + businessInfo.value = {...res.data} + }) +} +// 鍚堝悓閲戦 +const analysisCustomer = () => { + analysisCustomerContractAmounts().then((res) => { + sum.value = res.data.sum + yny.value = res.data.yny + chain.value = res.data.chain + // 涓烘瘡涓暟鎹」鍒嗛厤闅忔満棰滆壊 + materialPieSeries.value[0].data = res.data.item.map(item => ({ + ...item, + itemStyle: { color: getRandomColor() } + })) + }) +} +// 寰呭姙浜嬮」 +const todoInfoS = () => { + homeTodos().then((res) => { + todoList.value = res.data + }) +} +// 搴斾粯搴旀敹缁熻 +const statisticsReceivable = (type) => { + console.log(type) + statisticsReceivablePayable({type: radio1.value}).then((res) => { + barSeries.value[0].data = [ + { value: res.data.prepayMoney, itemStyle: { color: barColors2[0] } }, + { value: res.data.payableMoney, itemStyle: { color: barColors2[1] } }, + { value: res.data.advanceMoney, itemStyle: { color: barColors2[2] } }, + { value: res.data.receivableMoney, itemStyle: { color: barColors2[3] } } + ] + }) +} +// 璐ㄦ缁熻 +const qualityStatisticsInfo = () => { + qualityStatistics().then((res) => { + res.data.item.forEach(item => { + xAxis1.value[0].data.push(item.date) + barSeries1.value[0].data.push(item.supplierNum) + barSeries1.value[1].data.push(item.processNum) + barSeries1.value[2].data.push(item.factoryNum) + }) + qualityStatisticsObject.value.supplierNum = res.data.supplierNum + qualityStatisticsObject.value.processNum = res.data.processNum + qualityStatisticsObject.value.factoryNum = res.data.factoryNum + }) +} +const getAmountHalfYearNum = async () => { + const res = await getAmountHalfYear() + console.log(res) + const monthName = [] + const receiptAmount = [] + const invoiceAmount = [] + res.data.forEach(item => { + monthName.push(item.month) + receiptAmount.push(item.receiptAmount) + invoiceAmount.push(item.invoiceAmount) + }) + // 姝g‘鍝嶅簲寮忚祴鍊硷細鍒涘缓鏂扮殑 xAxis 鍜� series 瀵硅薄 + xAxis2.value[0].data = monthName + xAxis2.value[0].data = monthName.map(item => item.replace(/~/g, '\n~')); + lineSeries.value = [ + { + name: '寮�绁�', + type: 'line', + data: receiptAmount, + stack: 'Total', + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: 'rgba(131, 207, 255, 1)' + }, + { + offset: 1, + color: 'rgba(186, 228, 255, 1)' + } + ]) + }, + itemStyle: { + color: '#2D99FF', + borderColor: '#2D99FF' + }, + emphasis: { + focus: 'series' + }, + lineStyle: { + width: 0 + }, + showSymbol: true, + }, + { + name: '鍥炴', + type: 'line', + data: invoiceAmount, + stack: 'Total', + lineStyle: { + width: 0 + }, + itemStyle: { + color: '#83CFFF', + borderColor: '#83CFFF' + }, + showSymbol: true, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: 'rgba(54, 153, 255, 1)' + }, + { + offset: 1, + color: 'rgba(89, 169, 254, 1)' + } + ]) + }, + emphasis: { + focus: 'series' + }, + } + ] +} +</script> + +<style scoped> +.dashboard { + background: #f5f7fa; + min-height: 100vh; + padding: 20px; + box-sizing: border-box; +} +.dashboard-top { + display: flex; + gap: 20px; + margin-bottom: 20px; +} +.company-info { + display: flex; + flex-direction: column; + gap: 8px; + padding: 20px; + min-width: 0; + background-color: #EFF2FB; /* 浣跨敤鎸囧畾鐨勮儗鏅鑹� */ + background-image: url("../assets/images/denglu.png"); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + border-radius: 12px; + height: 138px; +} +.avatar { + width: 60px; + height: 60px; + border-radius: 50%; + object-fit: contain; + background: #fff; + border: 1px solid #eee; +} +.company-card { + display: flex; + flex-direction: column; + gap: 10px; + position: relative; + padding-right: 15px; +} + +.company-card::after { + content: ''; + position: absolute; + right: 0; + top: 0; + bottom: 0; + width: 1px; + background-color: #C9C5C5; + border-radius: 2px; +} +.company-name { + font-weight: 400; + font-size: 16px; + color: #161A9A; +} +.company-meta { + font-weight: 400; + font-size: 12px; + color: #818185; +} +.data-cards { + display: flex; + gap: 16px; + justify-content: flex-start; + background: #ffffff; + border-radius: 12px; + padding: 20px; +} +.data-title { + font-weight: 700; + font-size: 26px; + color: #FFFFFF; +} +.data-num { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 20px; +} +.data-card { + background: #fff; + border-radius: 12px; + padding: 14px; + min-width: 160px; + box-shadow: 0 2px 8px #eee; + display: flex; + flex-direction: column; + width: 32%; + height: 140px; +} +.data-card.sales { + background-image: url("../assets/images/xioashoushuju.png"); + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} +.data-card.purchase { + background-image: url("../assets/images/caigou.png"); + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} +.data-card.inventory { + background-image: url("../assets/images/kucun.png"); + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} +.data-desc { + font-weight: 500; + font-size: 13px; + color: #FFFFFF; +} +.data-value { + font-size: 18px; + font-weight: 500; + margin: 10px 0; + color: #FFFFFF; +} +.top-left { + display: flex; + flex-direction: column; + gap: 20px; + width: 50%; +} +.todo-panel { + background: #fff; + border-radius: 12px; + padding: 20px; + width: 50%; +} +.todo-list { + list-style: none; + padding: 0; + margin: 0; + font-size: 15px; + overflow-y: auto; + height: 260px; +} +.todo-list li { + border-radius: 8px; + margin-bottom: 12px; + padding: 8px 20px; + height: 74px; + display: flex; + justify-content: space-between; + align-items: center; + background: rgba(225,227,250,0.62); +} +.todo-title { + font-weight: 400; + font-size: 12px; + color: #000000; + position: relative; +} +.todo-title::before { + content: ''; /* 蹇呴渶锛岃〃绀鸿繖閲屾湁涓�涓唴瀹� */ + position: absolute; + left: -10px; /* 瀹氫綅鍒板乏渚� */ + top: 50%; /* 鍨傜洿灞呬腑 */ + transform: translateY(-50%); /* 寰皟鍨傜洿灞呬腑 */ + width: 6px; /* 鍦嗙殑鐩村緞 */ + height: 6px; /* 鍦嗙殑鐩村緞 */ + background: #498CEB; + border-radius: 50%; /* 璁╁叾鍙樻垚鍦嗗舰 */ +} +.todo-division { + font-weight: 400; + font-size: 12px; + color: #000000; +} +.todo-time { + font-weight: 400; + font-size: 12px; + color: #000000; +} +.todo-meta { + color: #888; + font-size: 13px; +} +.dashboard-row { + display: flex; + gap: 20px; + margin-bottom: 20px; +} +.main-panel { + background: #fff; + border-radius: 12px; + padding: 20px; + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; +} +.section-title { + position: relative; + font-size: 18px; + color: #333; + padding-left: 10px; + margin-bottom: 10px; + font-weight: 700; +} + +.section-title::before { + position: absolute; + left: 0; + top: 4px; + content: ''; + width: 4px; + height: 18px; + background-color: #002FA7; + border-radius: 2px; +} +.contract-info { + display: flex; + align-items: center; + gap: 20px; + height: 90px; + background: rgba(245,245,245,0.59); + width: 100%; + border-radius: 10px; + padding: 10px 30px; +} +.contract-summary { + display: flex; + align-items: center; + gap: 30px; +} +.contract-card { + display: flex; + flex-direction: column; + gap: 10px; +} +.contract-name { + font-weight: 400; + font-size: 14px; + color: #050505; +} +.contract-meta { + display: flex; + align-items: center; + width: 100%; + gap: 80px; +} +.main-amount { + font-size: 24px; + color: rgba(51,50,50,0.85); +} +.up { color: #e57373; } +.contract-list { + margin-top: 16px; + font-size: 14px; + color: #666; + list-style: none; + padding: 0; +} +.line { + position: relative; + width: 250px; +} +.line::after { + content: ''; + position: absolute; + right: 12px; + top: 0; + bottom: 0; + width: 1px; + background-color: #C9C5C5; + border-radius: 2px; +} +.contract-list li { + margin-top: 10px; +} +.quality-cards { + display: flex; + gap: 12px; + margin-bottom: 12px; +} +.quality-card { + border-radius: 8px; + padding: 15px 10px 10px 50px; + font-weight: 400; + font-size: 12px; + color: rgba(0,0,0,0.67); + width: 236px; + height: 49px; + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} +.quality-card.one { + background-image: url("../assets/images/yuancailiao.png"); +} +.quality-card.two { + background-image: url("../assets/images/guocheng.png"); +} +.quality-card.three { + background-image: url("../assets/images/chuchang.png"); + +} +.quality-card span { + color: #4fc3f7; + font-weight: bold; + margin-left: 6px; +} +.chart { + width: 100%; + height: 220px; + margin-top: 10px; +} +</style> \ No newline at end of file -- Gitblit v1.9.3