| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 |
- <template>
- <div class="page">
- <div class="top adfac">
- <div class="top-pre">
- <div class="top-pre-num adfacjb">
- <div class="top-pre-num-l">
- <p>支付订单数</p>
- <p class="n">{{ headData?.payOrderCount??0 }}</p>
- </div>
- <div class="top-pre-num-r">
- <img src="@/assets/images/agent/order_img1.png">
- </div>
- </div>
- <div class="top-pre-text adfac">
- <div class="tn">较上周 {{ headData?.increasePayOrderCount??0 }}% <i class="el-icon-caret-top" style="color: #33A7A7;"></i></div>
- </div>
- </div>
- <div class="top-pre">
- <div class="top-pre-num adfacjb">
- <div class="top-pre-num-l">
- <p>支付人数</p>
- <p class="n">{{ headData?.payUserCount??0 }}</p>
- </div>
- <div class="top-pre-num-r">
- <img src="@/assets/images/agent/order_img2.png">
- </div>
- </div>
- <div class="top-pre-text adfac">
- <div class="tn">较上周 {{ headData?.increasePayUserCount??0 }}% <i class="el-icon-caret-top" style="color: #33A7A7;"></i></div>
- </div>
- </div>
- <div class="top-pre">
- <div class="top-pre-num adfacjb">
- <div class="top-pre-num-l">
- <p>支付金额(元)</p>
- <p class="n">{{ headData?.totalOrderAmount??0 }}</p>
- </div>
- <div class="top-pre-num-r">
- <img src="@/assets/images/agent/order_img3.png">
- </div>
- </div>
- <div class="top-pre-text adfac">
- <div class="tn half">基础版 {{ headData?.baseOrderAmount??0 }}</div>
- <div class="tn half">专业版 {{ headData?.proOrderAmount??0 }}</div>
- </div>
- </div>
- <div class="top-pre">
- <div class="top-pre-num adfacjb">
- <div class="top-pre-num-l">
- <p>专业版总销量(次数)</p>
- <p class="n">{{ headData?.proSaleCount??0 }}</p>
- </div>
- <div class="top-pre-num-r">
- <img src="@/assets/images/agent/order_img4.png">
- </div>
- </div>
- <div class="top-pre-text adfac">
- <div class="tn half">待使用 {{ headData?.proWaitCount??0 }}</div>
- <div class="tn half">已使用 {{ headData?.proUsedCount??0 }}</div>
- </div>
- </div>
- <div class="top-pre">
- <div class="top-pre-num adfacjb">
- <div class="top-pre-num-l">
- <p>基础版总销量(次数)</p>
- <p class="n">{{ headData?.baseSaleCount??0 }}</p>
- </div>
- <div class="top-pre-num-r">
- <img src="@/assets/images/agent/order_img5.png">
- </div>
- </div>
- <div class="top-pre-text adfac">
- <div class="tn half">待使用 {{ headData?.baseWaitCount??0 }}</div>
- <div class="tn half">已使用 {{ headData?.baseUsedCount??0 }}</div>
- </div>
- </div>
- </div>
- <div class="middle adfacjb">
- <div class="box adffc">
- <div class="box-title">订单统计</div>
- <div class="box-echart">
- <div ref="ddtjRef" style="width: 100%; height: 100%;"></div>
- </div>
- </div>
- <div class="box adffc">
- <div class="box-title">支付人数</div>
- <div class="box-echart">
- <div ref="zfrsRef" style="width: 100%; height: 100%;"></div>
- </div>
- </div>
- </div>
- <div class="bottom">
- <div class="date adfac">
- <div class="date-pre" :class="{'active':didx==1}" @click="changeDate(1)">近7天</div>
- <div class="date-pre" :class="{'active':didx==2}" @click="changeDate(2)">本月</div>
- <el-date-picker @change="changeCusDate"
- v-model="queryParams.startEndTime"
- type="daterange"
- range-separator="至"
- start-placeholder="开始日期"
- end-placeholder="结束日期">
- </el-date-picker>
- </div>
- <div class="box adffc">
- <div class="box-title">订单金额</div>
- <div class="box-echart">
- <div ref="ddjeRef" style="width: 100%; height: 390px;"></div>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script setup name="">
- import * as echarts from "echarts";
- import {
- getOrderHeadData,
- getOrderCount,
- getPayUserCount,
- getOrderMoney
- } from '@/api/agent/indexTwo.js'
- import { ref, getCurrentInstance, onMounted } from 'vue'
- const { proxy } = getCurrentInstance();
-
- const didx = ref(1)
- const queryParams = ref({
- beginTime:'',
- endTime:'',
- startEndTime:'',
- type:1
- })
- const headData = ref({})
- const ddtjRef = ref(null)
- const zfrsRef = ref(null)
- const ddjeRef = ref(null)
- const changeDate = (idx) => {
- if(idx===didx.value) return
- didx.value = idx;
- queryParams.value.type = idx;
- queryParams.value.beginTime = '';
- queryParams.value.endTime = '';
- queryParams.value.startEndTime = '';
- getOrderMoneyFn()
- }
- const changeCusDate = (e) => {
- didx.value = '';
- queryParams.value.type = 3;
- queryParams.value.beginTime = proxy.parseTime(new Date(e[0]), '{yy}-{mm}-{dd}');
- queryParams.value.endTime = proxy.parseTime(new Date(e[1]), '{yy}-{mm}-{dd}');
- getOrderMoneyFn()
- }
- const getOrderCountFn = () => {
- getOrderCount({type:1}).then(res => {
- const { startDate, endDate } = getDateRange()
- let data = fillMissingDates(res.data,startDate,endDate)
- let xdata = data.map(item => item.dateDay)
- let ydata = data.map(item => item.stats)
- initDdtjEcharts(xdata,ydata)
- })
- }
- const getPayUserCountFn = () => {
- getPayUserCount({type:1}).then(res => {
- const { startDate, endDate } = getDateRange()
- let data = fillMissingDates(res.data,startDate,endDate)
- let xdata = data.map(item => item.dateDay)
- let ydata = data.map(item => item.stats)
- initZfrsEcharts(xdata,ydata)
- })
- }
- const getOrderMoneyFn = () => {
- let params = JSON.parse(JSON.stringify(queryParams.value))
- delete params.startEndTime;
- getOrderMoney(params).then(res => {
- let startDate = '', endDate = '';
- if(queryParams.value.type<3){
- startDate = getDateRange(queryParams.value.type).startDate;
- endDate = getDateRange(queryParams.value.type).endDate;
- }else{
- startDate = queryParams.value.beginTime;
- endDate = queryParams.value.endTime;
- }
- let data = fillMissingDates(res.data,startDate,endDate)
- let xdata = data.map(item => item.dateDay)
- let ydata = data.map(item => item.stats)
- initDdjeEcharts(xdata,ydata)
- })
- }
- const initDdtjEcharts = (xdata,ydata) => {
- const ddtjChart = echarts.init(ddtjRef.value)
- let option = {
- color: ['#33A7A7'],
- xAxis: {
- type: 'category',
- data: xdata
- },
- yAxis: {
- type: 'value'
- },
- grid: {
- left: '1%',
- right: '1%',
- top: '10%',
- bottom: '1%',
- containLabel: true
- },
- tooltip: {
- trigger: 'axis',
- // backgroundColor: '#FFFFFF',
- // textStyle: {
- // fontSize: 16
- // },
- // formatter: function (params) {
- // let result = params[0].name + '<br/>'
- // params.forEach(item => {
- // result += '订单笔数:' + item.value + '<br/>'
- // })
- // return result;
- // }
- },
- series: [
- {
- data: ydata,
- type: 'line',
- smooth: true,
- lineStyle: {
- width: 3
- },
- showSymbol: false,
- areaStyle: {
- opacity: 0.8,
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- {
- offset: 0,
- color: 'rgb(51,167,167,.1)'
- },
- {
- offset: 1,
- color: 'rgb(51,167,167,0)'
- }
- ])
- },
- }
- ]
- };
- ddtjChart.setOption(option)
- }
- const initZfrsEcharts = (xdata,ydata) => {
- const zfrsChart = echarts.init(zfrsRef.value)
- let option = {
- color: ['#33A7A7'],
- xAxis: {
- type: 'category',
- data: xdata
- },
- yAxis: {
- type: 'value'
- },
- tooltip: {
- trigger: 'axis'
- },
- grid: {
- left: '1%',
- right: '1%',
- top: '10%',
- bottom: '1%',
- containLabel: true
- },
- series: [
- {
- data: ydata,
- type: 'line',
- smooth: true,
- lineStyle: {
- width: 3
- },
- showSymbol: false,
- areaStyle: {
- opacity: 0.8,
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- {
- offset: 0,
- color: 'rgb(51,167,167,.1)'
- },
- {
- offset: 1,
- color: 'rgb(51,167,167,0)'
- }
- ])
- },
- }
- ]
- };
- zfrsChart.setOption(option)
- }
- const initDdjeEcharts = (xdata,ydata) => {
- const ddjeChart = echarts.init(ddjeRef.value)
- let option = {
- color: ['#33A7A7'],
- xAxis: {
- type: 'category',
- data: xdata
- },
- yAxis: {
- type: 'value'
- },
- tooltip: {
- trigger: 'axis'
- },
- grid: {
- left: '1%',
- right: '1%',
- top: '10%',
- bottom: '1%',
- containLabel: true
- },
- series: [
- {
- data: ydata,
- type: 'line',
- smooth: true,
- lineStyle: {
- width: 3
- },
- showSymbol: false,
- areaStyle: {
- opacity: 0.8,
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- {
- offset: 0,
- color: 'rgb(51,167,167,.1)'
- },
- {
- offset: 1,
- color: 'rgb(51,167,167,0)'
- }
- ])
- },
- }
- ]
- };
- ddjeChart.setOption(option)
- }
- /**
- * 填充指定日期范围内缺失的数据。
- * 该函数会确保从开始日期到结束日期的每一天都有一条数据记录。
- * 如果某天的数据不存在,则会使用指定的默认值进行填充。
- *
- * @param {Array<Object>} dataList - 原始数据数组,例如 [{stats: 2, dateDay: "2025-11-06"}]
- * @param {string} startDateStr - 开始日期字符串, 格式为 "YYYY-MM-DD"
- * @param {string} endDateStr - 结束日期字符串, 格式为 "YYYY-MM-DD"
- * @param {Object} options - (可选) 配置项
- * @param {string} options.dateKey - (可选) 数据对象中表示日期的属性名,默认为 'dateDay'
- * @param {Object} options.defaultValue - (可选) 缺失日期的默认数据对象,默认为 { stats: 0 }
- * @returns {Array<Object>} 填充完毕的完整数据数组
- */
- function fillMissingDates(dataList, startDateStr, endDateStr, options = {}) {
- const {
- dateKey = 'dateDay',
- defaultValue = { stats: 0 }
- } = options;
- const dataMap = new Map(dataList.map(item => [item[dateKey], item]));
- const result = [];
-
- // 注意:这里的 new Date() 也会受时区影响,但配合下面的循环和 formatLocalDate 可以正确工作
- const currentDate = new Date(startDateStr);
- const endDate = new Date(endDateStr);
- // 将 currentDate 的时间部分设置为中午12点,可以更稳妥地避免夏令时等边界问题
- currentDate.setHours(12, 0, 0, 0);
- endDate.setHours(12, 0, 0, 0);
- while (currentDate <= endDate) {
- const dateString = formatDate(currentDate);
- if (dataMap.has(dateString)) {
- result.push(dataMap.get(dateString));
- } else {
- result.push({
- ...defaultValue,
- [dateKey]: dateString,
- });
- }
- // 将日期增加一天
- currentDate.setDate(currentDate.getDate() + 1);
- }
- return result;
- }
- /**
- * 格式化日期对象为 'YYYY.MM.DD' 格式的字符串
- * @param {Date} date - 需要格式化的日期对象
- * @returns {string} - 格式化后的日期字符串
- */
- function 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}`;
- }
- /**
- * 根据类型获取指定的日期范围
- * @param {number} type - 类型 (1: 近7天, 2: 本月, 3: 本周)
- * @returns {{startDate: string, endDate: string} | {startDate: null, endDate: null}}
- */
- function getDateRange(type=1) {
- const today = new Date();
- // 为了避免跨天等时区问题,将时间统一设置为当天的零点
- today.setHours(0, 0, 0, 0);
- let startDate = new Date(today);
- const endDate = new Date(today); // 结束日期在所有情况下都是今天
- switch (type) {
- case 1:
- // --- 近7天 ---
- // 开始日期 = 今天 - 6天 (总共7天)
- startDate.setDate(today.getDate() - 6);
- break;
- case 2:
- // --- 本月 (从1号到今天) ---
- // 直接将开始日期的日设置为1号
- startDate.setDate(1);
- break;
- case 3:
- // --- 本周 (从周一到今天) ---
- // getDay() 返回 0(周日)-6(周六)。我们希望周一是1,周日是7
- const dayOfWeek = today.getDay() === 0 ? 7 : today.getDay();
- // 开始日期 = 今天 - (今天是本周第几天 - 1)
- startDate.setDate(today.getDate() - (dayOfWeek - 1));
- break;
- default:
- console.error("无效的类型参数,请输入 1, 2, 或 3。");
- return { startDate: null, endDate: null };
- }
- return {
- startDate: formatDate(startDate),
- endDate: formatDate(endDate),
- };
- }
- onMounted(()=>{
- getOrderHeadData().then(res=>{
- headData.value = res.data;
- })
- getOrderCountFn()
- getPayUserCountFn()
- getOrderMoneyFn()
- proxy.$nextTick(()=>{
-
- })
- })
- </script>
- <style scoped lang="scss">
- .page{
- padding: 16px;
- .top{
- justify-content: space-between;
- &-pre{
- width: calc(20% - 9.6px);
- background: #FFFFFF;
- box-shadow: 0px 2px 12px 0px rgba(0,0,0,0.06);
- border-radius: 6px;
- padding: 24px 20px;
- box-sizing: border-box;
- &-num{
- &-l{
- p{
- font-family: PingFangSC, PingFang SC;
- font-weight: 400;
- font-size: 14px;
- color: #335368;
- line-height: 14px;
- &.n{
- font-family: DINAlternate, DINAlternate;
- font-weight: bold;
- font-size: 28px;
- color: #002846;
- line-height: 28px;
- margin-top: 16px;
- }
- }
- }
- &-r{
- img{
- width: 60px;
- height: 60px;
- }
- }
- }
- &-text{
- margin-top: 16px;
- .tn{
- width: 100%;
- font-family: PingFangSC, PingFang SC;
- font-weight: 400;
- font-size: 14px;
- color: #667E90;
- line-height: 14px;
- &.half{
- width: 50%;
- }
- }
- }
- }
- }
- .box{
- background: #FFFFFF;
- box-shadow: 0px 2px 12px 0px rgba(0,0,0,0.06);
- border-radius: 6px;
- padding: 20px;
- box-sizing: border-box;
- }
- .box-title{
- font-family: PingFang-SC, PingFang-SC;
- font-weight: bold;
- font-size: 16px;
- color: #002846;
- line-height: 16px;
- }
- .box-echart{
- flex: 1;
- margin-top: 10px;
- }
- .middle{
- margin-top: 16px;
- .box{
- width: calc(50% - 6px);
- height: 390px;
- }
- }
- .bottom{
- margin-top: 16px;
- position: relative;
- }
- .date{
- position: absolute;
- top: 20px;
- right: 20px;
- &-pre{
- width: 54px;
- height: 40px;
- border-radius: 6px;
- border: 1px solid #DDE0E6;
- font-family: PingFangSC, PingFang SC;
- font-weight: 400;
- font-size: 14px;
- color: #335368;
- line-height: 40px;
- text-align: center;
- margin-right: 16px;
- cursor: pointer;
- &.active{
- background: rgba(51,167,167,0.08);
- border-radius: 6px;
- border: 1px solid #33A7A7;
- color: #009191;
- }
- }
- }
- }
- </style>
|