交易分析页面开发(无接口)

This commit is contained in:
郭永超 2025-06-14 17:19:04 +08:00
parent b06e15f490
commit 5c89ab4ea6

View File

@ -1,7 +1,13 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<div class="app-container-data"> <div class="app-container-data">
<div class="app-container-title">运营看板</div> <div class="app-container-title">运营看板
<el-radio-group v-model="dateRadio" @change="changeDateRadio" size="small" style="float: right;">
<el-radio-button label="周" :value="1" />
<el-radio-button label="月" :value="2" />
<el-radio-button label="年" :value="3" />
</el-radio-group>
</div>
<div style="display: flex"> <div style="display: flex">
<div class="app-container-data-left"> <div class="app-container-data-left">
<div class="app-container-data-left-top"> <div class="app-container-data-left-top">
@ -14,7 +20,7 @@
/> />
<div>销售总额()</div> <div>销售总额()</div>
</div> </div>
<div class="number">4,541.00</div> <div class="number">{{ topLeftData.salesTotalAmount }}</div>
</div> </div>
<div> <div>
<div class="title"> <div class="title">
@ -25,7 +31,7 @@
/> />
<div>订单总数()</div> <div>订单总数()</div>
</div> </div>
<div class="number">45</div> <div class="number">{{ topLeftData.orderTotalNum }}</div>
</div> </div>
</div> </div>
<div class="app-container-data-left-bottom"> <div class="app-container-data-left-bottom">
@ -38,7 +44,7 @@
/> />
<div>浏览量()</div> <div>浏览量()</div>
</div> </div>
<div class="number">61,151</div> <div class="number">{{ topLeftData.viewCount }}</div>
</div> </div>
<div> <div>
<div class="title"> <div class="title">
@ -49,7 +55,7 @@
/> />
<div>成功退款金额()</div> <div>成功退款金额()</div>
</div> </div>
<div class="number">541.00</div> <div class="number">{{ topLeftData.refundSuccessAmout }}</div>
</div> </div>
</div> </div>
</div> </div>
@ -153,19 +159,21 @@
<div class="bottom-box-summary-title">退款金额</div> <div class="bottom-box-summary-title">退款金额</div>
</div> </div>
</div> </div>
<div class="bottom-box-summary" style="line-height: 40px;margin: 0;" <div style="overflow-y: auto; height: 200px;">
v-for="(item, index) in afterSalesData.data" :key="index"> <div class="bottom-box-summary" style="line-height: 40px;margin: 0;"
<div class="bottom-box-summary-item" style="width: 30%; display: flex;"> v-for="(item, index) in afterSalesData.data" :key="index">
<img :src="item.goodUrl" style="width: 40px;height: 40px;" alt="" /> <div class="bottom-box-summary-item" style="width: 30%; display: flex;">
<div class="bottom-box-summary-title text-ellipsis" style="flex: 1; vertical-align: top;color: #000;margin-left: 8px;" title="耿马西红柿耿马西红柿耿马西红柿耿马西红柿"> <img :src="item.goodUrl" style="width: 40px;height: 40px;" alt="" />
{{ item.goodInfo }} <div class="bottom-box-summary-title text-ellipsis" style="flex: 1; vertical-align: top;color: #000;margin-left: 8px;" title="耿马西红柿耿马西红柿耿马西红柿耿马西红柿">
{{ item.goodInfo }}
</div>
</div>
<div class="bottom-box-summary-item" style="width: 100px;flex: initial;">
<div class="bottom-box-summary-title" style="color: #000;">{{ item.refundedOrders }}</div>
</div>
<div class="bottom-box-summary-item" style="width: 100px;flex: initial;">
<div class="bottom-box-summary-title" style="color: #000;">{{ item.refundAmount }}</div>
</div> </div>
</div>
<div class="bottom-box-summary-item" style="width: 100px;flex: initial;">
<div class="bottom-box-summary-title" style="color: #000;">{{ item.refundedOrders }}</div>
</div>
<div class="bottom-box-summary-item" style="width: 100px;flex: initial;">
<div class="bottom-box-summary-title" style="color: #000;">{{ item.refundAmount }}</div>
</div> </div>
</div> </div>
@ -176,7 +184,7 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, ref } from "vue"; import { onMounted, ref, onBeforeUnmount } from "vue";
import Mock from "mockjs"; import Mock from "mockjs";
import tableComponent from "@/components/tableComponent.vue"; import tableComponent from "@/components/tableComponent.vue";
import * as echarts from "echarts"; import * as echarts from "echarts";
@ -187,14 +195,19 @@ import img3 from "@/assets/images/third.png";
import alert from "@/assets/images/alert.png"; import alert from "@/assets/images/alert.png";
let tomato = ref("http://gov-cloud.oss-cn-chengdu.aliyuncs.com/backend/a866613495ed4678957a4440b8d3776c.png"); let tomato = ref("http://gov-cloud.oss-cn-chengdu.aliyuncs.com/backend/a866613495ed4678957a4440b8d3776c.png");
// DOM // DOM
const chartRef = ref(null); const chartRef = ref(null);
// ECharts // ECharts
let chartInstance = null; let chartInstance = null;
// //
const colorList = ['#3685FE', '#FFD500', '#25BF82']; const colorList = ['#3685FE', '#FFD500', '#25BF82', '#FF7F00'];
// x // x
const xData = ['1日', '2月', '3月', '4月', '5月', '6月', '7月']; const xData = ref([]);
const totalSalesData = ref([]); //
const totalOrdersData = ref([]); //
const pageViewData = ref([]); //
const refundAmountData = ref([]); //退
// //
const option = { const option = {
@ -225,8 +238,8 @@ const option = {
show: true, show: true,
backgroundColor: '#fff', backgroundColor: '#fff',
color: '#556677', color: '#556677',
borderColor: 'rgba(0,0,0,0)', borderColor: 'rgba(0,0,0)',
shadowColor: 'rgba(0,0,0,0)', shadowColor: 'rgba(0,0,0,0.3)',
shadowOffsetY: 0, shadowOffsetY: 0,
}, },
lineStyle: { lineStyle: {
@ -241,7 +254,7 @@ const option = {
extraCssText: 'box-shadow: 1px 0 2px 0 rgba(163,163,163,0.5); text-align: left;', extraCssText: 'box-shadow: 1px 0 2px 0 rgba(163,163,163,0.5); text-align: left;',
formatter: function (params) { formatter: function (params) {
let result = params[0].name + '<br>'; // x"1" let result = params[0].name + '<br>'; // x"1"
params.forEach((param) => { params.forEach((param,index) => {
// param.color // param.color
const colorDot = `<span style=" const colorDot = `<span style="
display:inline-block; display:inline-block;
@ -249,9 +262,19 @@ const option = {
border-radius:50%; border-radius:50%;
width:10px; width:10px;
height:10px; height:10px;
background-color:${param.color}"> background-color:${colorList[index]}">
</span>`; </span>`;
result += `${colorDot} ${param.seriesName}: ${param.value}${param.seriesName === '茎秆高度' ? 'cm' : param.seriesName === '叶片温度' ? '℃' : 'mm'} <br>`; let unit = "";
if(param.seriesName == '销售总额') {
unit = "元";
} else if(param.seriesName == '订单总数') {
unit = "个";
} else if(param.seriesName == '浏览量') {
unit = "人次";
} else if(param.seriesName == '成功退款金额') {
unit = "元";
}
result += `${colorDot} ${param.seriesName}: ${param.value} ${unit} <br>`;
}); });
return result; return result;
}, },
@ -260,12 +283,12 @@ const option = {
top: '20%', top: '20%',
left: 20, left: 20,
right: 20, right: 20,
bottom: 30, // bottom: 40, //
}, },
xAxis: [ xAxis: [
{ {
type: 'category', type: 'category',
data: xData, data: xData.value,
axisLine: { axisLine: {
lineStyle: { lineStyle: {
color: 'rgba(107,107,107,0.37)', color: 'rgba(107,107,107,0.37)',
@ -405,7 +428,7 @@ const option = {
{ {
name: '销售总额', name: '销售总额',
type: 'line', type: 'line',
data: [514.14, 468.18, 988.35, 1204.84, 954.16, 651.24, ], data: totalSalesData.value,
symbolSize: 1, symbolSize: 1,
symbol: 'circle', symbol: 'circle',
smooth: true, smooth: true,
@ -413,7 +436,7 @@ const option = {
showSymbol: false, showSymbol: false,
lineStyle: { lineStyle: {
width: 3, width: 3,
color: '#25BF82', color: '#3685FE',
shadowColor: 'rgba(158,135,255, 0.3)', shadowColor: 'rgba(158,135,255, 0.3)',
shadowBlur: 10, shadowBlur: 10,
shadowOffsetY: 20, shadowOffsetY: 20,
@ -426,36 +449,36 @@ const option = {
{ {
name: '订单总数', name: '订单总数',
type: 'line', type: 'line',
data: [4, 18, 29, 33, 18, 10, 28], data: totalOrdersData.value,
symbolSize: 1, symbolSize: 1,
symbol: 'circle', symbol: 'circle',
smooth: true, smooth: true,
yAxisIndex: 1, yAxisIndex: 1,
showSymbol: false, showSymbol: false,
lineStyle: { lineStyle: {
width: 5, width: 3,
color: '#FFD500', color: '#FFD500',
shadowColor: 'rgba(115,221,255, 0.3)', shadowColor: 'rgba(115,221,255, 0.3)',
shadowBlur: 10, shadowBlur: 10,
shadowOffsetY: 20, shadowOffsetY: 20,
}, },
itemStyle: { itemStyle: {
color: colorList[2], color: colorList[1],
borderColor: colorList[2], borderColor: colorList[1],
}, },
}, },
{ {
name: '浏览量', name: '浏览量',
type: 'line', type: 'line',
data: [1589, 2648, 5289, 4289, 9654, 6487, 5968], data: pageViewData.value,
symbolSize: 1, symbolSize: 1,
symbol: 'circle', symbol: 'circle',
smooth: true, smooth: true,
yAxisIndex: 2, yAxisIndex: 2,
showSymbol: false, showSymbol: false,
lineStyle: { lineStyle: {
width: 5, width: 3,
color: '#3685FE', color: '#25BF82',
shadowColor: 'rgba(115,221,255, 0.3)', shadowColor: 'rgba(115,221,255, 0.3)',
shadowBlur: 10, shadowBlur: 10,
shadowOffsetY: 20, shadowOffsetY: 20,
@ -468,22 +491,22 @@ const option = {
{ {
name: '成功退款金额', name: '成功退款金额',
type: 'line', type: 'line',
data: [62.44, 14.15, 17.48, 25.18, 21.91, 10.02, 2.25], data: refundAmountData.value,
symbolSize: 1, symbolSize: 1,
symbol: 'circle', symbol: 'circle',
smooth: true, smooth: true,
yAxisIndex: 3, yAxisIndex: 3,
showSymbol: false, showSymbol: false,
lineStyle: { lineStyle: {
width: 5, width: 3,
color: '#3685FE', color: '#FF7F00',
shadowColor: 'rgba(115,221,255, 0.3)', shadowColor: 'rgba(115,221,255, 0.3)',
shadowBlur: 10, shadowBlur: 10,
shadowOffsetY: 20, shadowOffsetY: 20,
}, },
itemStyle: { itemStyle: {
color: colorList[2], color: colorList[3],
borderColor: colorList[2], borderColor: colorList[3],
}, },
}, },
], ],
@ -515,6 +538,166 @@ const resizeChart = () => {
chartInstance.resize(); chartInstance.resize();
} }
}; };
// -
const loadEchartsData = async () => {
try {
let response = await getGoodManageInfo({
queryStartTime: startDate.value,
queryEndTime: endDate.value
});
console.log(response);
if (response.code == 200) {
option.xAxis[0].data = response.data.xData;
option.series[0].data = response.data.totalSalesData;
option.series[1].data = response.data.totalOrdersData;
option.series[2].data = response.data.pageViewData;
option.series[3].data = response.data.refundAmountData;
chartInstance.setOption(option);
} else {
echartsMockData(7);
}
} catch (error) { }
};
const echartsMockData = (count = 7) => {
console.log("右上角图表数据更新");
const xzData = generateDateArray(count)
let obj = Mock.mock({
// x (/)
xData: xzData,
// (500-50002)
[`totalSalesData|${count}`]: ['@float(100, 500, 2, 2)'],
// (1-100)
[`totalOrdersData|${count}`]: ['@integer(10, 100)'],
// (1000-10000)
[`pageViewData|${count}`]: ['@integer(1000, 10000)'],
// 退 (1-1002)
[`refundAmountData|${count}`]: ['@float(10, 100, 2, 2)']
})
option.xAxis[0].data = obj.xData;
option.series[0].data = obj.totalSalesData;
option.series[1].data = obj.totalOrdersData;
option.series[2].data = obj.pageViewData;
option.series[3].data = obj.refundAmountData;
console.log(option);
chartInstance.setOption(option);
};
const generateDateArray = (count) => {
const result = []
const today = new Date()
for (let i = count - 1; i >= 0; i--) {
const date = new Date(today)
date.setDate(date.getDate() - i)
const month = (date.getMonth() + 1).toString().padStart(2, '0')
const day = date.getDate().toString().padStart(2, '0')
result.push(`${month}-${day}`)
}
return result
}
//
let dateRadio = ref(1);
let startDate = ref('');
let endDate = ref('');
const changeDateRadio = (val) => {
console.log(val);
// val 1 2 3
let date = generateDateRange(val);
console.log(date);
startDate.value = date.startDate;
endDate.value = date.endDate;
//
loadTopLeftData();
echartsMockData(7); //
// loadEchartsData(); //
loadMiddleData();
loadbottomLeftData();
loadbottomRightData();
// -----------
let day = 7;
if (val == 1) {
day = 7;
} else if (val == 2) {
day = 30;
} else if (val == 3) {
day = 365;
}
echartsMockData(day);
};
/**
* 根据给定的时间范围类型生成相应的日期范围
* @param {number} type - 日期范围类型1:按周 2:按月 3:按年
* @returns {Object} 包含开始日期和结束日期的对象格式为YYYY-MM-DD
*/
function generateDateRange(type) {
//
const today = new Date();
//
const endDate = new Date(today);
//
let startDate = new Date(today);
//
switch(type) {
case 1: //
startDate.setDate(today.getDate() - 6); // 7
break;
case 2: //
startDate.setDate(today.getDate() - 29); // 30
break;
case 3: //
startDate.setDate(today.getDate() - 364); // 365
break;
default:
//
throw new Error('无效的参数类型请输入1(周)、2(月)或3(年)');
}
// YYYY-MM-DD
const formatDate = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
//
return {
startDate: formatDate(startDate),
endDate: formatDate(endDate)
};
}
let topLeftData = ref({});
// -
const loadTopLeftData = async () => {
console.log('左上角数据更新');
topLeftData.value = Object.assign({}, {
salesTotalAmount: '4,541.00', //
orderTotalNum: '45', //
viewCount: '61,151', //
refundSuccessAmout: '541.00', //退
});
// try {
// let response = await getGoodManageInfo({
// queryStartTime: startDate.value,
// queryEndTime: endDate.value
// });
// if (response.code == 200) {
// topLeftData.value = response.data;
// }
// } catch (error) { }
};
// //
const middleData = ref([]); const middleData = ref([]);
@ -523,18 +706,18 @@ const generateMockData = () => {
"list|5": [ "list|5": [
{ // { //
"id|+1": 10000, "id|+1": 10000,
title: '@pick(["买家数量", "支付转化率", "客单价", "退款率", "加购数量"])', title: '@pick(["买家数量", "支付转化率", "客单价", "退款率", "加购数量"])', //
value: "@float(10, 200, 2, 2)", value: "@float(10, 200, 2, 2)", //
unit: '@pick(["人", "%", "元", "%", "次"])', unit: '@pick(["人", "%", "元", "%", "次"])', //
tooltip: '@pick(["购买商品人数统计", "支付转化率 = 完成支付的订单数量 + 访问网站的用户数量 x 100%", "客单价是指每一位顾客平均购买商品金额", "退款率 = (退款订单数量 + 总订单数量) x 100%", "用户将商品加入购物车次数"])', tooltip: '@pick(["购买商品人数统计", "支付转化率 = 完成支付的订单数量 + 访问网站的用户数量 x 100%", "客单价是指每一位顾客平均购买商品金额", "退款率 = (退款订单数量 + 总订单数量) x 100%", "用户将商品加入购物车次数"])', //
ratioMethod: '@pick(["环比", "同比"])', // ratioMethod: '@pick(["环比", "同比"])', //
ratioStatus: '@pick(["上升", "下降"])', // ratioStatus: '@pick(["上升", "下降"])', //
ratioValue: '@float(10, 20, 1, 1)' + '%', // ratioValue: '@float(10, 20, 1, 1)' + '%', //
// //
"ranking|+1": 1, // "ranking|+1": 1, //
goodUrl: tomato.value, goodUrl: tomato.value, //
goodName: "@cname", goodName: "@cname", //
stockToUseRatio: "@float(10, 200, 2, 2)" + "%", // stockToUseRatio: "@float(10, 200, 2, 2)" + "%", //
salesAmount: "@integer(1000, 20000)" + "元", // salesAmount: "@integer(1000, 20000)" + "元", //
}, },
@ -542,7 +725,7 @@ const generateMockData = () => {
}).list; }).list;
}; };
// //
const tableData = ref([]); const tableData = ref([]);
const columns = ref([ const columns = ref([
{ prop: "ranking", label: "排名", slotName: "ranking", width: 66 }, { prop: "ranking", label: "排名", slotName: "ranking", width: 66 },
@ -551,36 +734,72 @@ const columns = ref([
{ prop: "stockToUseRatio", label: "存销比" }, { prop: "stockToUseRatio", label: "存销比" },
{ prop: "salesAmount", label: "销售金额" }, { prop: "salesAmount", label: "销售金额" },
]); ]);
//
const afterSalesData = ref({});
const afterSalesData = ref({
// -
const loadMiddleData = async () => {
console.log('中间数据更新');
middleData.value = generateMockData();
// try {
// let response = await getGoodManageInfo({
// queryStartTime: startDate.value,
// queryEndTime: endDate.value
// });
// console.log(response);
// if (response.code == 200) {
// middleData.value = response.data;
// }
// } catch (error) { }
};
// -
const loadbottomLeftData = async () => {
console.log('左下角数据更新');
tableData.value = generateMockData();
// try {
// let response = await getGoodManageInfo({
// queryStartTime: startDate.value,
// queryEndTime: endDate.value
// });
// console.log(response);
// if (response.code == 200) {
// tableData.value = response.data;
// }
// } catch (error) { }
};
// -
const loadbottomRightData = async () => {
console.log('右下角数据更新');
afterSalesData.value = {
totalAfterSalesOrders: 1128, totalAfterSalesOrders: 1128,
totalRefundOrders: 265, totalRefundOrders: 265,
totalReturnOrders: 129, totalReturnOrders: 129,
data: [ data: [
{ goodUrl: tomato.value, goodInfo: "耿马西红柿", refundedOrders: "25单", refundAmount: '129元' }, { goodUrl: tomato.value, goodInfo: "耿马西红柿", refundedOrders: "25单", refundAmount: '129元' },
{ goodUrl: tomato.value, goodInfo: "耿马西红柿", refundedOrders: "36单", refundAmount: '265元' }, { goodUrl: tomato.value, goodInfo: "耿马西红柿", refundedOrders: "36单", refundAmount: '265元' },
{ goodUrl: tomato.value, goodInfo: "耿马西红柿", refundedOrders: "51单", refundAmount: '1128元' }, { goodUrl: tomato.value, goodInfo: "耿马西红柿", refundedOrders: "51单", refundAmount: '146元' },
{ goodUrl: tomato.value, goodInfo: "耿马西红柿", refundedOrders: "25单", refundAmount: '239元' },
{ goodUrl: tomato.value, goodInfo: "耿马西红柿", refundedOrders: "36单", refundAmount: '365元' },
{ goodUrl: tomato.value, goodInfo: "耿马西红柿", refundedOrders: "51单", refundAmount: '198元' },
], ],
}); };
//
const loadMiddleData = async () => {
middleData.value = generateMockData();
tableData.value = generateMockData();
console.log(tableData.value);
// try { // try {
// let response = await getGoodManageInfo(prams); // let response = await getGoodManageInfo({
// queryStartTime: startDate.value,
// queryEndTime: endDate.value
// });
// console.log(response);
// if (response.code == 200) { // if (response.code == 200) {
// middleData.value = response.data.records; // tableData.value = response.data;
// middleData.value = response.data.total;
// } // }
// } catch (error) { } // } catch (error) { }
}; };
onMounted(() => {
initChart(); onMounted(async() => {
loadMiddleData(); await initChart();
changeDateRadio(1);
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -588,7 +807,7 @@ onMounted(() => {
width: 200px; width: 200px;
display: flex; display: flex;
align-items: center; align-items: center;
margin-right: 50px; margin-right: 20px;
font-size: 18px; font-size: 18px;
color: #999999; color: #999999;
} }
@ -599,7 +818,7 @@ onMounted(() => {
} }
.number { .number {
margin-right: 100px; margin-right: 100px;
margin-top: 20px; margin-top: 16px;
font-size: 18px; font-size: 18px;
font-weight: bold; font-weight: bold;
color: #000000; color: #000000;
@ -613,6 +832,7 @@ onMounted(() => {
} }
.app-container-title { .app-container-title {
margin-bottom: 20px; margin-bottom: 20px;
padding-right: 10px;
font-size: 20px; font-size: 20px;
font-weight: bold; font-weight: bold;
} }
@ -622,17 +842,17 @@ onMounted(() => {
width: 100%; width: 100%;
background-color: #fff; background-color: #fff;
.app-container-data-left { .app-container-data-left {
width: 40%; width: 500px;
.app-container-data-left-top { .app-container-data-left-top {
display: flex; display: flex;
} }
.app-container-data-left-bottom { .app-container-data-left-bottom {
margin-top: 50px; margin-top: 40px;
display: flex; display: flex;
} }
} }
.app-container-data-right { .app-container-data-right {
width: 60%; flex: 1;
} }
} }
.app-container-block { .app-container-block {
@ -675,7 +895,7 @@ onMounted(() => {
width: 50%; width: 50%;
background-color: #fff; background-color: #fff;
border-radius: 10px; border-radius: 10px;
padding: 20px 20px 20px 20px; padding: 20px 20px 0px 20px;
} }
.bottom-box-item-title { .bottom-box-item-title {
font-size: 18px; font-size: 18px;
@ -701,6 +921,9 @@ onMounted(() => {
} }
} }
} }
.custom-table-container {
padding-bottom: 0;
}
} }
} }
.text-ellipsis { .text-ellipsis {