660 lines
18 KiB
Vue
660 lines
18 KiB
Vue
<template>
|
|
<div class="app-container home">
|
|
|
|
<!-- <el-divider /> -->
|
|
<el-row :gutter="20">
|
|
<el-col :span="11">
|
|
<el-card class="update-log" >
|
|
<template v-slot:header>
|
|
<div class="titleBox">
|
|
<div class="line"></div>
|
|
<div class="name">
|
|
<div>配送订单总览</div>
|
|
<el-date-picker
|
|
v-model="dateInventoryTypeObj"
|
|
type="daterange"
|
|
range-separator="至"
|
|
start-placeholder="开始时间"
|
|
end-placeholder="结束时间"
|
|
value-format="YYYY-MM-DD"
|
|
style="margin: 0 20px;"
|
|
/>
|
|
<el-button type="primary" size="small" @click="getTotalOverview">搜索</el-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<div class="body" id="orderOverview">
|
|
</div>
|
|
</el-card>
|
|
</el-col>
|
|
<el-col :span="13">
|
|
<el-card class="update-log" >
|
|
<template v-slot:header>
|
|
<div class="titleBox">
|
|
<div class="line"></div>
|
|
<div class="name">
|
|
<div>配送费统计</div>
|
|
<el-date-picker
|
|
v-model="twoTimeArr"
|
|
type="daterange"
|
|
range-separator="至"
|
|
start-placeholder="开始时间"
|
|
end-placeholder="结束时间"
|
|
value-format="YYYY-MM-DD"
|
|
style="margin: 0 20px;"
|
|
/>
|
|
<el-button type="primary" size="small" @click="getFee">搜索</el-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<div class="body" id="orderFee">
|
|
</div>
|
|
</el-card>
|
|
</el-col>
|
|
|
|
</el-row>
|
|
<el-row :gutter="20" style="margin-top: 20px;">
|
|
|
|
<el-col :span="13">
|
|
<el-card class="update-log" >
|
|
<template v-slot:header>
|
|
<div class="titleBox">
|
|
<div class="line"></div>
|
|
<div class="name">
|
|
<div>每日订单统计</div>
|
|
<!-- <el-date-picker
|
|
v-model="dateInventoryTypeObj"
|
|
type="daterange"
|
|
range-separator="至"
|
|
start-placeholder="开始时间"
|
|
end-placeholder="结束时间"
|
|
value-format="YYYY-MM-DD"
|
|
style="margin: 0 20px;"
|
|
/> -->
|
|
<el-select v-model="monthValue" placeholder="选择月份" style="width: 200px;margin: 0 20px;">
|
|
<el-option
|
|
v-for="item in options"
|
|
:key="item.value"
|
|
:label="item.label"
|
|
:value="item.value"
|
|
/>
|
|
</el-select>
|
|
<el-button type="primary" size="small" @click="getDayOrder('search')">搜索</el-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<div class="body" id="everyDayCount">
|
|
</div>
|
|
</el-card>
|
|
</el-col>
|
|
<el-col :span="11" >
|
|
<el-card class="update-log" >
|
|
<template v-slot:header>
|
|
<div class="titleBox">
|
|
<div class="line"></div>
|
|
<div class="name">
|
|
<div>各类车型配送统计</div>
|
|
<el-date-picker
|
|
v-model="threeTimeArr"
|
|
type="daterange"
|
|
range-separator="至"
|
|
start-placeholder="开始时间"
|
|
end-placeholder="结束时间"
|
|
value-format="YYYY-MM-DD"
|
|
style="margin: 0 20px;"
|
|
/>
|
|
<el-button type="primary" size="small" @click="getOrderCarType">搜索</el-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<div class="body" id="orderCarType">
|
|
</div>
|
|
</el-card>
|
|
</el-col>
|
|
</el-row>
|
|
<!-- @click="goAi" -->
|
|
<div class="imgBox" ref="iconRef" @mousedown="startDrag" :style="{
|
|
left: position.x ? position.x + 'px' :'',
|
|
top: position.y + 'px'
|
|
}" >
|
|
<img src="@/assets/images/rgzn.png" style="width: 100%;height: 100%;" @click="goAi" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup name="Index">
|
|
import * as echarts from 'echarts';
|
|
import {getOrderStatus,getDeliveryFee,getOrderCar,everyDayOrder} from "@/api/index/index";
|
|
const version = ref('3.9.0')
|
|
import { parseTime } from '@/utils/ruoyi'
|
|
// import { getTime } from '@/utils/index'
|
|
import { get } from '@vueuse/core';
|
|
// let statDate = parseTime(new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), '{y}-{m}-{d}') // 14天前
|
|
// let endDate = parseTime(new Date(), '{y}-{m}-{d}') // 当前时间
|
|
|
|
let currentDate = new Date();
|
|
let currentYear = currentDate.getFullYear();
|
|
let monthVal = currentDate.getMonth() + 1;
|
|
let statDate = parseTime(new Date(currentYear,monthVal-1,1), '{y}-{m}-{d}')
|
|
let endDate = parseTime(new Date(currentYear,monthVal,0), '{y}-{m}-{d}')
|
|
|
|
|
|
// let statDate = parseTime(getTime('start'))
|
|
// let endDate = parseTime(getTime(''))
|
|
const dateInventoryTypeObj = ref([statDate,endDate]) //第一个接口时间参数
|
|
const twoTimeArr = ref([statDate,endDate]) //第二个接口时间参数
|
|
const threeTimeArr = ref([statDate,endDate]) //第三个接口、各类车型 时间参数
|
|
const fourTimeArr = ref([statDate,endDate]) //第四个接口、每日订单时间参数
|
|
const { proxy } = getCurrentInstance()
|
|
const options = [
|
|
{ value: '01', label: '1月' },
|
|
{ value: '02', label: '2月' },
|
|
{ value: '03', label: '3月' },
|
|
{ value: '04', label: '4月' },
|
|
{ value: '05', label: '5月' },
|
|
{ value: '06', label: '6月' },
|
|
{ value: '07', label: '7月' },
|
|
{ value: '08', label: '8月' },
|
|
{ value: '09', label: '9月' },
|
|
{ value: '10', label: '10月' },
|
|
{ value: '11', label: '11月' },
|
|
{ value: '12', label: '12月' }
|
|
]
|
|
const monthValue = ref('')
|
|
onMounted(() => {
|
|
getTotalOverview()
|
|
getFee()
|
|
getOrderCarType()
|
|
getDayOrder('') //每日订单统计
|
|
});
|
|
//第一个接口,订单总览
|
|
function getTotalOverview(){
|
|
|
|
const dataArr = {
|
|
statDate: dateInventoryTypeObj.value[0],
|
|
endDate: dateInventoryTypeObj.value[1]
|
|
}
|
|
getOrderStatus(dataArr).then(Response => {
|
|
const dataInfo = Response.data
|
|
const data = [
|
|
{name:'已完成',value:dataInfo.signedCount},
|
|
{name:'配送中',value:dataInfo.deliveringCount},
|
|
{name:'待配送',value:dataInfo.waitCount},
|
|
]
|
|
getChart(data)
|
|
// console.log(Response)
|
|
})
|
|
}
|
|
//第二个接口,各月配送费用
|
|
function getFee(){
|
|
const dataArr = {
|
|
statDate: twoTimeArr.value[0],
|
|
endDate: twoTimeArr.value[1]
|
|
}
|
|
getDeliveryFee(dataArr).then(Response => {
|
|
// console.log(Response)
|
|
getFeeChart(Response.data)
|
|
})
|
|
}
|
|
//第三个接口,各类车型订单数
|
|
function getOrderCarType(){
|
|
const dataArr = {
|
|
statDate: threeTimeArr.value[0],
|
|
endDate: threeTimeArr.value[1]
|
|
}
|
|
getOrderCar(dataArr).then(Response => {
|
|
const dataInfo = Response.data
|
|
getCarTypeChart(dataInfo)
|
|
// const data = [
|
|
// {name:'小车',value:dataInfo.smallCarCount},
|
|
// {name:'中车',value:dataInfo.middleCarCount},
|
|
// ]
|
|
})
|
|
}
|
|
//第四个接口,每日订单数
|
|
function getDayOrder(isSearch){
|
|
|
|
// monthValue.value = monthVal+'' //下拉框显示
|
|
|
|
if(isSearch == 'search'){
|
|
if(monthValue.value == ''){
|
|
proxy.$modal.msgError("请选择月份");
|
|
return
|
|
}
|
|
|
|
|
|
// console.log(currentYear)
|
|
fourTimeArr.value[0] = parseTime(new Date(currentYear,monthValue.value-1,1), '{y}-{m}-{d}')
|
|
fourTimeArr.value[1] = parseTime(new Date(currentYear,monthValue.value,0), '{y}-{m}-{d}')
|
|
|
|
}
|
|
else{
|
|
monthValue.value = monthVal+''
|
|
}
|
|
// console.log(11111)
|
|
// console.log(dateInventoryTypeObj.vlaue[0])
|
|
const dataArr = {
|
|
statDate: fourTimeArr.value[0],
|
|
endDate: fourTimeArr.value[1]
|
|
}
|
|
// console.log(dataArr)
|
|
everyDayOrder(dataArr).then(Response => {
|
|
const dataInfo = Response.data
|
|
getEveryDayChart(dataInfo)
|
|
})
|
|
}
|
|
//第一个图表
|
|
function getChart(data) {
|
|
let chartDom = document.getElementById('orderOverview');
|
|
let myChart = echarts.init(chartDom, null, {
|
|
// width: 600, // 宽度
|
|
height: 300 // 高度
|
|
});
|
|
// console.log(data)
|
|
myChart.setOption({
|
|
tooltip: {
|
|
trigger: 'item'
|
|
},
|
|
legend: {
|
|
orient: 'vertical',
|
|
left: 'left'
|
|
},
|
|
series: [
|
|
{
|
|
name: '',
|
|
type: 'pie',
|
|
radius: '50%',
|
|
data: data,
|
|
emphasis: {
|
|
itemStyle: {
|
|
shadowBlur: 10,
|
|
shadowOffsetX: 0,
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
}
|
|
}
|
|
}
|
|
]
|
|
});
|
|
}
|
|
//第二个图表,各月配送费用
|
|
function getFeeChart(dataInfo){
|
|
// dataInfo = [
|
|
// {statMonth:'2025-01',totalActualFee:1000},
|
|
// {statMonth:'2025-02',totalActualFee:1221},
|
|
// {statMonth:'2025-03',totalActualFee:1543},
|
|
// {statMonth:'2025-04',totalActualFee:2000},
|
|
// {statMonth:'2025-05',totalActualFee:699},
|
|
// {statMonth:'2025-06',totalActualFee:4000},
|
|
// {statMonth:'2025-07',totalActualFee:6000},
|
|
// {statMonth:'2025-08',totalActualFee:5000},
|
|
// {statMonth:'2025-09',totalActualFee:3000},
|
|
// {statMonth:'2025-10',totalActualFee:2000},
|
|
// {statMonth:'2025-11',totalActualFee:1288},
|
|
// {statMonth:'2025-12',totalActualFee:1000},
|
|
// ]
|
|
var chartDom = document.getElementById('orderFee');
|
|
var myChart = echarts.init(chartDom, null, {
|
|
// width: 600, // 宽度
|
|
height: 300 // 高度
|
|
});
|
|
var option;
|
|
let xAxisData = []
|
|
let seriesData = []
|
|
if(dataInfo.length > 0){
|
|
dataInfo.forEach(item => {
|
|
xAxisData.push(item.statMonth)
|
|
seriesData.push(item.totalActualFee)
|
|
})
|
|
}
|
|
option = {
|
|
xAxis: {
|
|
type: 'category',
|
|
data: xAxisData,
|
|
axisLabel: {
|
|
rotate: 30,
|
|
},
|
|
},
|
|
yAxis: {
|
|
type: 'value'
|
|
},
|
|
series: [
|
|
{
|
|
data: seriesData,
|
|
type: 'line',
|
|
label: {
|
|
show: true, // 显示数值
|
|
position: 'top', // 数值显示的位置,可以是'top', 'bottom', 'left', 'right'等
|
|
formatter: '{c}',
|
|
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
option && myChart.setOption(option);
|
|
}
|
|
//第三个图表,各类车型订单数
|
|
function getCarTypeChart(dataInfo){
|
|
let xAxisData = [] //车型
|
|
let totalOrder = [] //订单数
|
|
let totalActualFee = [] //总费用
|
|
let totalKm = [] //总里程
|
|
if(dataInfo.length > 0){
|
|
dataInfo.forEach(item => {
|
|
xAxisData.push(item.vehicleTypeName)
|
|
totalOrder.push(item.orderCount)
|
|
totalActualFee.push(item.totalActualFee)
|
|
totalKm.push(item.totalKm)
|
|
})
|
|
}
|
|
|
|
var chartDom = document.getElementById('orderCarType');
|
|
var myChart = echarts.init(chartDom, null, {
|
|
// width: 600, // 宽度
|
|
height: 300 // 高度
|
|
});
|
|
myChart.setOption({
|
|
// title: {
|
|
// text: 'World Population'
|
|
// },
|
|
tooltip: {
|
|
trigger: 'axis',
|
|
axisPointer: {
|
|
type: 'shadow'
|
|
}
|
|
},
|
|
grid: {
|
|
// bottom: '8%',
|
|
},
|
|
legend: {},
|
|
yAxis: {
|
|
type: 'value',
|
|
boundaryGap: [0, 0.01]
|
|
},
|
|
xAxis: {
|
|
type: 'category',
|
|
data: xAxisData,
|
|
axisLabel: {
|
|
rotate: 30,
|
|
},
|
|
},
|
|
series: [
|
|
{
|
|
name: '订单数',
|
|
type: 'bar',
|
|
data: totalOrder
|
|
},
|
|
{
|
|
name: '总费用',
|
|
type: 'bar',
|
|
data: totalActualFee
|
|
},
|
|
{
|
|
name: '公里数',
|
|
type: 'bar',
|
|
data: totalKm
|
|
}
|
|
]
|
|
});
|
|
}
|
|
//第四个图表,每日订单数
|
|
function getEveryDayChart(dataInfo){
|
|
// let xAxisData = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31] //日期
|
|
// let totalOrder = [10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10] //订单数
|
|
// let totalActualFee = [20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20] //总费用
|
|
// let totalKm = [30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30] //总里程
|
|
let xAxisData = [] //日期
|
|
let totalOrder = [] //订单数
|
|
let totalActualFee = [] //总费用
|
|
let totalKm = [] //总里程
|
|
if(dataInfo.length > 0){
|
|
dataInfo.forEach(item => {
|
|
xAxisData.push(item.statDate)
|
|
totalOrder.push(item.orderCount)
|
|
totalActualFee.push(item.totalActualFee)
|
|
totalKm.push(item.totalKm)
|
|
})
|
|
}
|
|
|
|
var chartDom = document.getElementById('everyDayCount');
|
|
var myChart = echarts.init(chartDom, null, {
|
|
// width: 600, // 宽度
|
|
height: 300 // 高度
|
|
});
|
|
myChart.setOption({
|
|
// title: {
|
|
// text: 'World Population'
|
|
// },
|
|
tooltip: {
|
|
trigger: 'axis',
|
|
axisPointer: {
|
|
type: 'shadow'
|
|
}
|
|
},
|
|
grid: {
|
|
// bottom: '8%',
|
|
},
|
|
legend: {},
|
|
yAxis: {
|
|
type: 'value',
|
|
boundaryGap: [0, 0.01]
|
|
},
|
|
xAxis: {
|
|
type: 'category',
|
|
data: xAxisData,
|
|
axisLabel: {
|
|
// rotate: 50,
|
|
},
|
|
},
|
|
series: [
|
|
{
|
|
name: '订单数',
|
|
type: 'bar',
|
|
data: totalOrder
|
|
},
|
|
{
|
|
name: '总费用',
|
|
type: 'bar',
|
|
data: totalActualFee
|
|
},
|
|
{
|
|
name: '公里数',
|
|
type: 'bar',
|
|
data: totalKm
|
|
}
|
|
]
|
|
});
|
|
}
|
|
function goTarget(url) {
|
|
window.open(url, '__blank')
|
|
}
|
|
|
|
// 图标元素引用
|
|
const iconRef = ref(null)
|
|
|
|
// 图标当前位置
|
|
const position = reactive({ x: 0, y: 300 })
|
|
|
|
// 拖拽状态
|
|
let isDragging = false
|
|
let offsetX = 0
|
|
let offsetY = 0
|
|
let isMove
|
|
// 获取边界限制
|
|
function getBoundaries() {
|
|
if (!iconRef.value) return { minX: 0, maxX: 0, minY: 0, maxY: 0 }
|
|
const rect = iconRef.value.getBoundingClientRect()
|
|
const width = rect.width
|
|
const height = rect.height
|
|
|
|
// 浏览器视口尺寸
|
|
const viewportWidth = window.innerWidth
|
|
const viewportHeight = window.innerHeight
|
|
|
|
return {
|
|
minX: 0,
|
|
maxX: viewportWidth - width, // 最大 left 值
|
|
minY: 0,
|
|
maxY: viewportHeight - height // 最大 top 值
|
|
}
|
|
}
|
|
// 开始拖拽
|
|
function startDrag(e) {
|
|
// console.log(1111)
|
|
if (!iconRef.value) return
|
|
isDragging = true
|
|
isMove = false
|
|
const rect = iconRef.value.getBoundingClientRect()
|
|
offsetX = e.clientX - rect.left
|
|
offsetY = e.clientY - rect.top
|
|
|
|
e.preventDefault()
|
|
}
|
|
|
|
// 全局鼠标移动监听
|
|
function handleMouseMove(e) {
|
|
if (!isDragging || !iconRef.value) return
|
|
isMove = true
|
|
const boundaries = getBoundaries()
|
|
let newX = e.clientX - offsetX
|
|
let newY = e.clientY - offsetY
|
|
|
|
// 限制在视口内
|
|
// console.log(boundaries.minX+'--'+)
|
|
newX = Math.max(boundaries.minX, Math.min(newX, boundaries.maxX))
|
|
newY = Math.max(boundaries.minY, Math.min(newY, boundaries.maxY))
|
|
|
|
position.x = newX
|
|
position.y = newY
|
|
}
|
|
|
|
// 停止拖拽
|
|
function handleMouseUp() {
|
|
// console.log(2222)
|
|
// if(!isMove && iconRef.value){
|
|
// window.open('http://192.168.1.28:8000/#/chat/index', '_blank')
|
|
// }
|
|
isDragging = false
|
|
}
|
|
|
|
// 绑定全局事件(组件挂载时)
|
|
window.addEventListener('mousemove', handleMouseMove)
|
|
window.addEventListener('mouseup', handleMouseUp)
|
|
|
|
// 可选:组件卸载时清理(防止内存泄漏)
|
|
// 在 Vue 3 中,如果使用 <script setup>,可以用 onBeforeUnmount
|
|
import { onBeforeUnmount } from 'vue'
|
|
onBeforeUnmount(() => {
|
|
window.removeEventListener('mousemove', handleMouseMove)
|
|
window.removeEventListener('mouseup', handleMouseUp)
|
|
})
|
|
|
|
function goAi(){
|
|
// console.log(33333)
|
|
// setTimeout(() => {
|
|
if(!isMove){
|
|
window.open('http://192.168.1.28:8000/#/chat/index', '_blank')
|
|
}
|
|
// }, 200);
|
|
// window.open('http://192.168.1.28:8000/#/chat/index', '_blank')
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.home {
|
|
blockquote {
|
|
padding: 10px 20px;
|
|
margin: 0 0 20px;
|
|
font-size: 17.5px;
|
|
border-left: 5px solid #eee;
|
|
}
|
|
hr {
|
|
margin-top: 20px;
|
|
margin-bottom: 20px;
|
|
border: 0;
|
|
border-top: 1px solid #eee;
|
|
}
|
|
.col-item {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
ul {
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
font-size: 13px;
|
|
color: #676a6c;
|
|
overflow-x: hidden;
|
|
|
|
ul {
|
|
list-style-type: none;
|
|
}
|
|
|
|
h4 {
|
|
margin-top: 0px;
|
|
}
|
|
|
|
h2 {
|
|
margin-top: 10px;
|
|
font-size: 26px;
|
|
font-weight: 100;
|
|
}
|
|
|
|
p {
|
|
margin-top: 10px;
|
|
|
|
b {
|
|
font-weight: 700;
|
|
}
|
|
}
|
|
|
|
.update-log {
|
|
ol {
|
|
display: block;
|
|
list-style-type: decimal;
|
|
margin-block-start: 1em;
|
|
margin-block-end: 1em;
|
|
margin-inline-start: 0;
|
|
margin-inline-end: 0;
|
|
padding-inline-start: 40px;
|
|
}
|
|
}
|
|
.titleBox{
|
|
display: flex;
|
|
align-items: center;
|
|
.line {
|
|
width: 6px;
|
|
height: 15px;
|
|
background: #199793;
|
|
border-radius: 12px 12px 12px 12px;
|
|
transform: rotateX(360deg);
|
|
}
|
|
.name{
|
|
margin-left: 8px;
|
|
display: flex;
|
|
align-items: center;
|
|
height: 28px;
|
|
font-family: PingFang SC, PingFang SC;
|
|
font-weight: 500;
|
|
font-size: 20px;
|
|
color: #333333;
|
|
line-height: 23px;
|
|
text-align: left;
|
|
font-style: normal;
|
|
text-transform: none;
|
|
}
|
|
}
|
|
.imgBox{
|
|
width: 40px;
|
|
height: 40px;
|
|
position:fixed;
|
|
right: 0;
|
|
// top: 50%;
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
</style>
|
|
|