统计,bug修改

This commit is contained in:
BabyBoy 2025-09-17 15:29:45 +08:00
parent c0b08fc2da
commit e05280aa4b
12 changed files with 2117 additions and 9 deletions

View File

@ -29,8 +29,8 @@
"animate.css": "4.1.1", "animate.css": "4.1.1",
"await-to-js": "3.0.0", "await-to-js": "3.0.0",
"axios": "1.8.4", "axios": "1.8.4",
"dayjs": "^1.11.10",
"crypto-js": "4.2.0", "crypto-js": "4.2.0",
"dayjs": "^1.11.10",
"echarts": "5.6.0", "echarts": "5.6.0",
"element-plus": "2.9.8", "element-plus": "2.9.8",
"file-saver": "2.0.5", "file-saver": "2.0.5",
@ -41,6 +41,7 @@
"moment": "^2.24.0", "moment": "^2.24.0",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"pinia": "3.0.2", "pinia": "3.0.2",
"qs": "^6.10.3",
"screenfull": "6.0.2", "screenfull": "6.0.2",
"vue": "3.5.13", "vue": "3.5.13",
"vue-count-to": "1.0.13", "vue-count-to": "1.0.13",
@ -49,10 +50,10 @@
"vue-json-pretty": "2.4.0", "vue-json-pretty": "2.4.0",
"vue-router": "4.5.0", "vue-router": "4.5.0",
"vue-types": "6.0.0", "vue-types": "6.0.0",
"vxe-table": "4.13.7", "vxe-table": "4.13.7"
"qs": "^6.10.3"
}, },
"devDependencies": { "devDependencies": {
"@antv/g2": "^4.2.11",
"@iconify/json": "^2.2.276", "@iconify/json": "^2.2.276",
"@types/crypto-js": "4.2.2", "@types/crypto-js": "4.2.2",
"@types/file-saver": "2.0.7", "@types/file-saver": "2.0.7",
@ -73,6 +74,8 @@
"globals": "16.0.0", "globals": "16.0.0",
"prettier": "3.5.2", "prettier": "3.5.2",
"sass": "1.87.0", "sass": "1.87.0",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.2",
"typescript": "~5.8.3", "typescript": "~5.8.3",
"unocss": "66.0.0", "unocss": "66.0.0",
"unplugin-auto-import": "19.1.2", "unplugin-auto-import": "19.1.2",
@ -84,9 +87,7 @@
"vite-plugin-svg-icons-ng": "^1.4.0", "vite-plugin-svg-icons-ng": "^1.4.0",
"vite-plugin-vue-devtools": "7.7.5", "vite-plugin-vue-devtools": "7.7.5",
"vitest": "3.1.2", "vitest": "3.1.2",
"vue-tsc": "^2.2.8", "vue-tsc": "^2.2.8"
"stylus": "^0.54.5",
"stylus-loader": "^3.0.2"
}, },
"overrides": { "overrides": {
"quill": "2.0.2" "quill": "2.0.2"

36
src/api/goods.js Normal file
View File

@ -0,0 +1,36 @@
import request from '@/utils/request'
// 订单统计概览
export function getOrderOverView(query) {
return request({
url: '/manager/statistics/order/overview',
method: 'get',
params: query
})
}
// 获取订单统计图表
export function getOrderChart(query) {
return request({
url: '/manager/statistics/order',
method: 'get',
params: query
})
}
// 统计相关订单统计
export function statisticsOrderList(query) {
return request({
url: '/manager/statistics/order/order',
method: 'get',
params: query
})
}
// 统计相关退单统计
export function statisticsOrderRefundList(query) {
return request({
url: '/manager/statistics/order/refund',
method: 'get',
params: query
})
}

38
src/api/member.js Normal file
View File

@ -0,0 +1,38 @@
import request from '@/utils/request'
// 获取首页查询热卖商品TOP
export function getMemberStatistics(query) {
return request({
url: '/manager/statistics/member',
method: 'get',
params: query
})
}
// 获取流量统计
export function getStatisticsList(query) {
return request({
url: '/manager/statistics/view/list',
method: 'get',
params: query
})
}
// 获取流量统计
export function goodsStatistics(query) {
return request({
url: '/manager/statistics/goods',
method: 'get',
params: query
})
}
//查询店铺列表
export function getShopListData(query) {
return request({
url: '/manager/store/store',
method: 'get',
params: query
})
}

View File

@ -0,0 +1,234 @@
<template>
<div>
<div class="breadcrumb">
<span @click="clickBreadcrumb(item, index)" :class="{ 'active': item.selected }" v-for="(item, index) in dateList" :key="index">
{{ item.title }}</span
>
<div class="date-picker">
<el-select
@change="changeSelect($event, selectedWay)"
v-model="month"
placeholder="年月查询"
clearable
style="width: 200px; margin-left: 10px"
>
<el-option v-for="(item, i) in dates" :value="item.year + '-' + item.month" :key="i" clearable>
{{ item.year + '年' + item.month + '月' }}
</el-option>
</el-select>
</div>
<!-- <div class="shop-list" v-if="!closeShop">
<Select clearable @on-change="changeshop(selectedWay)" v-model="storeId" placeholder="店铺查询" style="width: 200px; margin-left: 10px">
<Scroll :on-reach-bottom="handleReachBottom">
<Option v-for="(item, index) in shopsData" :value="item.id" :key="index">{{ item.storeName }}</Option>
</Scroll>
</Select>
</div> -->
</div>
</div>
</template>
<script>
import { getShopListData } from '@/api/member.js';
export default {
props: ['closeShop'],
data() {
return {
month: '', //
selectedWay: {
//
title: '过去7天',
selected: true,
searchType: 'LAST_SEVEN'
},
storeId: '', // id
dates: [], //
params: {
//
pageNumber: 1,
pageSize: 10,
storeName: ''
},
dateList: [
//
{
title: '今天',
selected: false,
searchType: 'TODAY'
},
{
title: '昨天',
selected: false,
searchType: 'YESTERDAY'
},
{
title: '过去7天',
selected: true,
searchType: 'LAST_SEVEN'
},
{
title: '过去30天',
selected: false,
searchType: 'LAST_THIRTY'
}
],
originDateList: [
//
{
title: '今天',
selected: false,
searchType: 'TODAY'
},
{
title: '昨天',
selected: false,
searchType: 'YESTERDAY'
},
{
title: '过去7天',
selected: true,
searchType: 'LAST_SEVEN'
},
{
title: '过去30天',
selected: false,
searchType: 'LAST_THIRTY'
}
],
shopTotal: '', //
shopsData: [] //
};
},
mounted() {
this.getFiveYears();
this.getShopList();
},
methods: {
//
// handleReachBottom() {
// setTimeout(() => {
// if (this.params.pageNumber * this.params.pageSize <= this.shopTotal) {
// this.params.pageNumber++;
// this.getShopList();
// }
// }, 1500);
// },
//
getShopList() {
// getShopListData(this.params).then((res) => {
// if (res.success) {
// /**
// *
// */
// this.shopTotal = res.result.total;
// this.shopsData.push(...res.result.records);
// }
// });
},
//
changeshop(val) {
this.selectedWay.storeId = this.storeId;
this.$emit('selected', this.selectedWay);
},
// 5
getFiveYears() {
const getYear = new Date().getFullYear();
const lastFiveYear = getYear - 5;
const maxMonth = new Date().getMonth() + 1;
const dates = [];
// 5
for (let year = lastFiveYear; year <= getYear; year++) {
for (let month = 1; month <= 12; month++) {
if (year == getYear && month > maxMonth) {
} else {
dates.push({
year: year,
month: month
});
}
}
}
this.dates = dates.reverse();
},
//
changeSelect(e) {
this.month = e;
console.log(this.month);
if (this.month) {
this.dateList.forEach((res) => {
res.selected = false;
});
this.selectedWay.year = this.month.split('-')[0];
this.selectedWay.month = this.month.split('-')[1];
this.selectedWay.searchType = '';
this.$emit('selected', this.selectedWay);
} else {
const current = this.dateList.find((item) => {
return item.selected;
});
this.selectedWay = current;
this.clickBreadcrumb(current);
this.$emit('selected', this.selectedWay);
}
},
//
clickBreadcrumb(item) {
let currentIndex;
this.dateList.forEach((res, index) => {
res.selected = false;
if (res.title === item.title) {
currentIndex = index;
}
});
item.selected = true;
item.storeId = this.storeId;
this.month = '';
if (item.searchType == '') {
const currentDate = this.originDateList[currentIndex].searchType;
if (currentDate) {
item.searchType = currentDate;
} else {
item.searchType = 'LAST_SEVEN';
}
}
this.selectedWay = item;
this.selectedWay.year = new Date().getFullYear();
this.selectedWay.month = '';
this.$emit('selected', this.selectedWay);
}
}
};
</script>
<style lang="scss" scoped>
.breadcrumb {
display: flex;
align-items: center;
> span {
margin-right: 15px;
cursor: pointer;
}
}
.active {
color: #ff5c58;
position: relative;
}
.date-picker {
}
.active:before {
content: '';
position: absolute;
bottom: -10px;
left: 0;
width: 100%;
height: 3px;
background: #ff5c58;
}
</style>

View File

@ -68,7 +68,6 @@ export default {
methods: { methods: {
initChart() { initChart() {
this.defaultOption = { ...this.defaultOption, ...this.chartData }; this.defaultOption = { ...this.defaultOption, ...this.chartData };
console.log(this.defaultOption);
const chartDom = document.getElementById(this.id); const chartDom = document.getElementById(this.id);
const myChart = echarts.init(chartDom); const myChart = echarts.init(chartDom);
myChart.setOption(this.defaultOption); myChart.setOption(this.defaultOption);

View File

@ -0,0 +1,123 @@
<template>
<div>
<!-- <Affix :offset-top="100"> -->
<el-card shadow="hover">
<affixTime :closeShop="true" @selected="clickBreadcrumb" />
</el-card>
<!-- </Affix> -->
<el-card shadow="hover">
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClickType">
<el-tab-pane label="热门商品订单数量" name="NUM">
<el-table :data="data" border empty-text="暂无数据">
<el-table-column label="商品名称" prop="goodsName" align="center" />
<el-table-column label="销售数量" prop="num" align="center" />
<el-table-column label="销售金额" prop="price" align="center" />
</el-table>
</el-tab-pane>
<el-tab-pane label="热门商品订单金额" name="PRICE">
<el-table :data="data" border empty-text="暂无数据">
<el-table-column label="商品名称" prop="goodsName" align="center" />
<el-table-column label="销售数量" prop="num" align="center" />
<el-table-column label="销售金额" prop="price" align="center" />
</el-table>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</template>
<script>
import * as API_Goods from '@/api/member';
import affixTime from '@/components/affix-time';
export default {
components: {
affixTime
},
data() {
return {
params: {
//
searchType: 'LAST_SEVEN',
year: '',
month: '',
shopId: '',
type: 'NUM'
},
activeName: 'NUM',
columns: [
//
{
title: '商品名称',
key: 'goodsName'
},
{
title: '销售数量',
key: 'num'
},
{
title: '销售金额',
key: 'price',
render: (h, params) => {
return h('priceColorScheme', { props: { value: params.row.price, color: this.$mainColor } });
}
}
],
data: [] //
};
},
methods: {
// tab
handleClickType(name) {
this.params.type = name;
this.getData();
},
//
clickBreadcrumb(item, index) {
const callback = item;
const type = this.params.type;
this.params = { ...callback };
this.params.type = type;
this.getData();
},
//
getData() {
Promise.all([API_Goods.goodsStatistics(this.params)]).then((res) => {
if (res[0].result) {
this.data = res[0].result;
}
});
}
},
mounted() {
this.getData();
}
};
</script>
<style scoped lang="scss">
.page-col {
text-align: right;
margin: 10px 0;
}
.order-col {
display: flex;
> div {
margin-right: 8px;
padding: 16px;
font-size: 15px;
}
}
.order-list {
display: flex;
}
.tips {
margin: 0 8px;
}
.card {
margin-bottom: 10px;
}
</style>

View File

@ -0,0 +1,226 @@
<template>
<div class="p-2">
<!-- <Affix :offset-top="100"> -->
<el-card shadow="hover">
<affixTime :closeShop="true" @selected="clickBreadcrumb" />
</el-card>
<!-- </Affix> -->
<el-card shadow="hover">
<div>
<h4>
客户增长趋势333
<el-button
style="margin-left: 10px"
@click="
() => {
enableShowMemberCount = !enableShowMemberCount;
initMemberChart();
}
"
size="small"
>{{ enableShowMemberCount ? '关闭' : '显示' }}用户总人数</el-button
>
</h4>
<div id="orderChart"></div>
</div>
</el-card>
<el-card shadow="hover">
<div>
<h4>客户增长报表</h4>
<el-table :data="data" border empty-text="暂无数据">
<el-table-column label="充值人" prop="createDate" align="center" />
<el-table-column label="当前会员" prop="memberCount" align="center" />
<el-table-column label="新增会员" prop="newlyAdded" align="center" />
<el-table-column label="活跃会员" prop="activeQuantity" align="center" />
</el-table>
</div>
</el-card>
</div>
</template>
<script>
import * as API_Member from '@/api/member';
import { Chart } from '@antv/g2';
import affixTime from '@/components/affix-time';
export default {
components: { affixTime },
data() {
return {
columns: [
//
{
key: 'createDate',
title: '日期',
sortable: true
},
{
key: 'memberCount',
title: '当前会员'
},
{
key: 'newlyAdded',
title: '新增会员'
},
{
key: 'activeQuantity',
title: '活跃会员'
}
],
//
dateList: [
{
title: '今天',
selected: false,
value: 'TODAY'
},
{
title: '昨天',
selected: false,
value: 'YESTERDAY'
},
{
title: '过去7天',
selected: true,
value: 'LAST_SEVEN'
},
{
title: '过去30天',
selected: false,
value: 'LAST_THIRTY'
}
],
year: '', //
orderChart: '', //
params: {
//
searchType: 'LAST_SEVEN',
year: '',
month: '',
shopId: ''
},
data: [], //
enableShowMemberCount: false
};
},
watch: {
params: {
handler(val) {
this.$nextTick(() => {
this.init();
});
},
deep: true
},
year(val) {
this.params.year = new Date(val).getFullYear();
}
},
methods: {
//
initMemberChart() {
// legend-filter
/**
* 将数据分成三组来进行展示
*/
const count = [];
const newly = [];
const actives = [];
this.data.forEach((item) => {
if (this.enableShowMemberCount && (item.memberCount != '' || item.memberCount != null)) {
count.push({
createDate: item.createDate,
memberCount: item.memberCount,
title: '当前会员数量'
});
}
if (!this.enableShowMemberCount && (item.newlyAdded != '' || item.newlyAdded != null)) {
newly.push({
createDate: item.createDate,
memberCount: item.newlyAdded,
title: '新增会员数量'
});
}
if (!this.enableShowMemberCount && (item.activeQuantity != '' || item.activeQuantity != null)) {
actives.push({
createDate: item.createDate,
memberCount: item.activeQuantity,
title: '当日活跃数量'
});
}
});
const data = [...count, ...newly, ...actives];
this.orderChart.data(data);
this.orderChart.scale({
activeQuantity: {
range: [0, 1],
nice: true
}
});
this.orderChart.tooltip({
showCrosshairs: true,
shared: true
});
this.orderChart.line().position('createDate*memberCount').color('title').label('memberCount').shape('smooth');
this.orderChart.point().position('createDate*memberCount').color('title').label('memberCount').shape('circle').style({
stroke: '#fff',
lineWidth: 1
});
this.orderChart.area().position('createDate*memberCount').color('title').shape('smooth');
this.orderChart.render();
},
//
clickBreadcrumb(item, index) {
const callback = item;
console.warn(callback);
this.params = { ...callback };
},
//
init() {
this.orderChart ? this.orderChart.clear() : '';
API_Member.getMemberStatistics(this.params).then((res) => {
if (res.result) {
res.result.forEach((item) => {
item.activeQuantity += '';
});
this.data = res.result;
if (!this.orderChart) {
this.orderChart = new Chart({
container: 'orderChart',
autoFit: true,
height: 500,
padding: [70, 70, 70, 70]
});
}
this.initMemberChart();
}
});
}
},
mounted() {
const data = new Date();
this.year = data;
this.$nextTick(() => {
this.init();
});
}
};
</script>
<style scoped lang="scss">
.wrapper {
padding-bottom: 200px;
}
.card {
margin-bottom: 10px;
}
</style>

View File

@ -0,0 +1,898 @@
<template>
<div class="p-2">
<!-- <Affix :offset-top="100"> -->
<el-card shadow="hover">
<affixTime :closeShop="true" @selected="clickBreadcrumb" />
</el-card>
<!-- </Affix> -->
<el-card shadow="hover">
<div>
<h4>交易概况</h4>
<div class="flex">
<div class="transactionList">
<div class="transaction-item" v-for="(item, index) in transactionList" :key="index">
<h4>{{ item.label }}</h4>
<div class="transaction-card" v-if="item.label == '转换'">
<div class="card-item">
<div class="card-item-label">访客数UV</div>
<div class="card-item-value">
{{ overViewList.uvNum || 0 }}
</div>
</div>
<div class="card-item">
<div class="card-item-label">下单转化率</div>
<div class="card-item-value">
{{ overViewList.orderConversionRate || '0%' }}
</div>
</div>
<div class="card-item">
<div class="card-item-label">付款转化率</div>
<div class="card-item-value">
{{ overViewList.paymentsConversionRate || '0%' }}
</div>
</div>
<div class="card-item">
<div class="card-item-label">全店转化率</div>
<div class="card-item-value">
{{ overViewList.overallConversionRate || '0%' }}
</div>
</div>
</div>
<div class="transaction-card" v-if="item.label == '订单'">
<div class="card-item">
<div class="card-item-label">下单笔数</div>
<div class="card-item-value">
{{ overViewList.orderNum || 0 }}
</div>
</div>
<div class="card-item">
<div class="card-item-label">下单人数</div>
<div class="card-item-value">
{{ overViewList.orderMemberNum || 0 }}
</div>
</div>
<div class="card-item">
<div class="card-item-label">下单金额</div>
<div class="card-item-value">
{{ overViewList.orderAmount || 0 || unitPrice('¥') }}
</div>
</div>
<div class="card-item">
<div class="card-item-label">付款笔数</div>
<div class="card-item-value">
{{ overViewList.paymentOrderNum || 0 }}
</div>
</div>
<div class="card-item">
<div class="card-item-label">付款人数</div>
<div class="card-item-value">
{{ overViewList.paymentsNum || 0 }}
</div>
</div>
<div class="card-item">
<div class="card-item-label">付款金额</div>
<div class="card-item-value">
{{ overViewList.paymentAmount || 0 || unitPrice('¥') }}
</div>
</div>
</div>
<div class="transaction-card" v-if="item.label == '退单'">
<div class="card-item">
<div class="card-item-label">退单笔数</div>
<div class="card-item-value">
{{ overViewList.refundOrderNum || 0 }}
</div>
</div>
<div class="card-item">
<div class="card-item-label">退单金额</div>
<div class="card-item-value">
{{ overViewList.refundOrderPrice || 0 || unitPrice('¥') }}
</div>
</div>
</div>
</div>
</div>
<div class="shap">
<div id="overViewChart">
<!-- -->
<div class="block">
<div class="box">
<span>访客数</span>
<span>{{ overViewList.uvNum || 0 }}</span>
</div>
</div>
<!-- -->
<div class="block">
<div class="box">
<span>下单笔数</span>
<span>{{ overViewList.orderNum || 0 }}</span>
</div>
</div>
<!-- -->
<div class="block">
<div class="box">
<span>付款笔数</span>
<span>{{ overViewList.paymentOrderNum || 0 }}</span>
</div>
</div>
<!-- 线 -->
<div class="rightBorder"></div>
<div class="leftTopBorder"></div>
<div class="leftBottomBorder"></div>
<!--数据 -->
<div class="leftTopTips">
<div>下单转化率</div>
<div>{{ overViewList.orderConversionRate || '0%' }}</div>
</div>
<div class="leftBottomTips">
<div>付款转化率</div>
<div>{{ overViewList.paymentsConversionRate || '0%' }}</div>
</div>
<div class="rightTips">
<div>整体转换率</div>
<div>{{ overViewList.overallConversionRate || '0%' }}</div>
</div>
</div>
</div>
</div>
</div>
</el-card>
<el-card shadow="hover">
<div>
<h4>交易趋势</h4>
<div></div>
</div>
<div>
<div id="orderChart"></div>
</div>
</el-card>
<el-card shadow="hover">
<div>
<h4>订退单统计</h4>
<div class="breadcrumb" style="margin-bottom: 20px">
<el-radio-group v-model="orderOrRefund" type="button" size="small" button-style="solid">
<el-radio :label="1">订单</el-radio>
<el-radio :label="0">退单</el-radio>
</el-radio-group>
</div>
<div>
<!-- 1订单 0退单 -->
<el-table :data="data" border empty-text="订单暂无数据" v-if="orderOrRefund == 1">
<el-table-column label="商家名称" prop="storeName" align="center" />
<el-table-column label="用户名" prop="memberName" align="center" />
<el-table-column label="订单状态" prop="orderStatus" align="center">
<template v-slot="scope">
{{ orderStatusList[scope.row.orderStatus] }}
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime" align="center" />
<el-table-column label="支付时间" prop="paymentTime" align="center" />
<el-table-column label="价格" prop="flowPrice" align="center">
<template v-slot="scope"> {{ scope.row.flowPrice || 0 }} </template>
</el-table-column>
</el-table>
<el-table :data="data" border empty-text="退单暂无数据" v-if="orderOrRefund == 0">
<el-table-column label="商品图片" prop="date" align="goodsImage">
<template v-slot="scope">
<el-image style="width: 100px; height: 100px" :src="scope.row.goodsImage" fit="cover"></el-image>
</template>
</el-table-column>
<el-table-column label="商品名称" prop="goodsName" align="center" />
<el-table-column label="商家名称" prop="storeName" align="center" />
<el-table-column label="售后单类型" prop="serviceType" align="center">
<template v-slot="scope">
{{ serviceTypeList[scope.row.serviceType] }}
</template>
</el-table-column>
<el-table-column label="售后单状态" prop="serviceStatus" align="center">
<template v-slot="scope">
{{ serviceStatusList[scope.row.serviceStatus] }}
</template>
</el-table-column>
<el-table-column label="退款时间" prop="refundTime" align="center" />
<el-table-column label="申请退款金额" prop="applyRefundPrice" align="center">
<template v-slot="scope"> {{ scope.row.applyRefundPrice }} </template>
</el-table-column>
<el-table-column label="实际金额" prop="flowPrice" align="center">
<template v-slot="scope"> {{ scope.row.flowPrice }} </template>
</el-table-column>
<el-table-column label="申请原因" prop="reason" align="center" />
</el-table>
</div>
<!-- (index) => {
refundParams.pageNumber = index;
} -->
<!-- (size) => {
(refundParams.pageSize = size), (refundParams.pageNumber = 1);
} -->
<!-- <Page
v-if="showRecords"
size="small"
@on-change="pageNumberChange"
@on-page-size-change="pageSizeChange"
class="mt_10"
show-total
show-sizer
show-elevator
:total="total"
/> -->
</div>
</el-card>
</div>
</template>
<script>
import * as API_Goods from '@/api/goods';
import { Chart } from '@antv/g2';
import orderRow from './order/orderDetail.vue';
import refundRow from './order/refundOrder.vue';
import affixTime from '@/components/affix-time';
export default {
components: { orderRow, refundRow, affixTime },
data() {
return {
orderOrRefund: 1, // 退
total: 0, //
//
orderStatusList: {
UNDELIVERED: '待发货',
UNPAID: '未付款',
PAID: '已付款',
DELIVERED: '已发货',
CANCELLED: '已关闭',
COMPLETED: '已完成',
TAKE: '已完成'
},
serviceTypeList: {
//
CANCEL: '取消',
RETURN_GOODS: '退货',
EXCHANGE_GOODS: '换货',
RETURN_MONEY: '退款'
},
serviceStatusList: {
//
APPLY: '申请售后',
PASS: '通过售后',
REFUSE: '拒绝售后',
BUYER_RETURN: '买家退货,待卖家收货',
SELLER_RE_DELIVERY: '商家换货/补发',
SELLER_CONFIRM: '卖家确认收货',
SELLER_TERMINATION: '卖家终止售后',
BUYER_CONFIRM: '买家确认收货',
BUYER_CANCEL: '买家取消售后',
WAIT_REFUND: '等待平台退款',
COMPLETE: '完成售后'
},
//
data: [], //退
columns: [], // 退title
orderColumns: [
//
{
type: 'expand',
width: 50,
render: (h, params) => {
return h(orderRow, {
props: {
res: params.row
}
});
}
},
{
title: '商家名称',
key: 'storeName'
},
{
title: '用户名',
key: 'memberName'
},
{
title: '订单状态',
key: 'orderStatus',
render: (h, params) => {
return h('div', this.orderStatusList[params.row.orderStatus]);
}
},
{
title: '创建时间',
key: 'createTime'
},
{
title: '支付时间',
key: 'paymentTime',
render: (h, params) => {
return h('div', params.row.paymentTime || '暂无');
}
},
{
title: '价格',
key: 'flowPrice',
render: (h, params) => {
return h('priceColorScheme', { props: { value: params.row.flowPrice, color: this.$mainColor } });
}
},
{
title: '操作',
key: 'action',
render: (h, params) => {
return h(
'Button',
{
props: {
size: 'small'
},
on: {
click: () => {
const { sn } = params.row;
this.$router.push({
query: { sn },
path: this.orderOrRefund == 1 ? 'order-detail' : 'after-order-detail' + '?sn=' + sn
});
}
}
},
'查看'
);
}
}
],
refundColumns: [
//
{
type: 'expand',
width: 50,
render: (h, params) => {
return h(refundRow, {
props: {
res: params.row
}
});
}
},
{
title: '商品图片',
key: 'goodsImage',
render: (h, params) => {
return h('img', {
attrs: {
src: params.row.goodsImage
},
style: {
width: '60px',
verticalAlign: 'middle'
}
});
}
},
{
title: '商品名称',
key: 'goodsName'
},
{
title: '商家名称',
key: 'storeName'
},
{
title: '售后单类型',
key: 'serviceType',
render: (h, params) => {
return h('div', this.serviceTypeList[params.row.serviceType]);
}
},
{
title: '售后单状态',
key: 'serviceStatus',
render: (h, params) => {
return h('div', this.serviceStatusList[params.row.serviceStatus]);
}
},
{
title: '退款时间',
key: 'refundTime',
render: (h, params) => {
return h('div', params.row.refundTime || '暂无');
}
},
{
title: '申请退款金额',
key: 'applyRefundPrice',
render: (h, params) => {
return h('priceColorScheme', { props: { value: params.row.applyRefundPrice, color: this.$mainColor } });
}
},
{
title: '申请原因',
key: 'reason'
},
{
title: '实际金额',
key: 'flowPrice',
render: (h, params) => {
return h('div', this.$options.filters.unitPrice(params.row.flowPrice ? params.row.flowPrice : 0, '¥'));
}
},
{
title: '操作',
key: 'action',
render: (h, params) => {
return h(
'Button',
{
props: {
size: 'small'
},
on: {
click: () => {
const { sn } = params.row;
this.$router.push({
query: { sn },
path: this.orderOrRefund == 1 ? 'order-detail' : 'after-order-detail' + '?sn=' + sn
});
}
}
},
'查看'
);
}
}
],
//
transactionList: [
{
label: '转换',
value: ''
},
{
label: '订单',
value: ''
},
{
label: '退单',
value: ''
}
],
chartList: [], //
orderChart: '', //
overViewList: {}, //
overViewChart: '', //
//
dateList: [
{
title: '今天',
selected: false,
value: 'TODAY'
},
{
title: '昨天',
selected: false,
value: 'YESTERDAY'
},
{
title: '过去7天',
selected: true,
value: 'LAST_SEVEN'
},
{
title: '过去30天',
selected: false,
value: 'LAST_THIRTY'
}
],
//
orderParams: {
searchType: 'LAST_SEVEN', // TODAY , YESTERDAY , LAST_SEVEN , LAST_THIRTY
year: '',
shopId: '',
memberId: ''
},
//
overViewParams: {
month: '',
searchType: 'LAST_SEVEN', // TODAY , YESTERDAY , LAST_SEVEN , LAST_THIRTY
storeId: '',
year: ''
},
defaultParams: {
month: '',
searchType: 'LAST_SEVEN', // TODAY , YESTERDAY , LAST_SEVEN , LAST_THIRTY
storeId: '',
year: ''
},
// 退
refundParams: {
pageNumber: 1,
pageSize: 10,
searchType: 'LAST_SEVEN',
storeId: '',
year: ''
},
showRecords: false
//
//
};
},
watch: {
refundParams: {
handler(val) {
this.getOrderList();
},
deep: true,
immediate: true
},
orderOrRefund() {
this.showRecords = false;
// this.$set(this.refundParams, 'pageNumber', 1);
this.refundParams.pageNumber = 1;
this.getOrderList();
},
orderParams: {
handler() {
this.$nextTick(() => {
this.initOrderChartList();
});
},
deep: true
},
overViewParams: {
handler() {
this.initOrderOverViewList();
},
deep: true
}
},
methods: {
unitPrice(val, unit, location) {
const price = this.formatPrice(val);
if (location === 'before') {
return price.substr(0, price.length - 3);
}
if (location === 'after') {
return price.substr(-2);
}
return (unit || '') + price;
},
formatPrice(price) {
if (typeof price !== 'number') return price;
return String(Number(price).toFixed(2)).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
},
pageNumberChange(val) {
this.refundParams.pageNumber = val;
this.getOrderList();
},
pageSizeChange(val) {
this.refundParams.pageSize = val;
this.getOrderList();
},
//
initOrderChart() {
// legend-filter
const data = this.chartList;
data.forEach((item) => {
item.createTime = item.createTime.split(' ')[0];
item.title = '交易额';
});
this.orderChart.data(data);
this.orderChart.tooltip({
showCrosshairs: true,
shared: true
});
this.orderChart.line().position('createTime*price').label('price').color('title').shape('smooth');
this.orderChart.point().position('createTime*price').label('price').color('title').shape('circle').style({
stroke: '#fff',
lineWidth: 1
});
this.orderChart.area().position('createTime*price').color('title').shape('smooth');
this.orderChart.render();
},
clickBreadcrumb(item, index) {
const callback = JSON.parse(JSON.stringify(item));
console.log('callback', callback);
this.orderParams = callback;
this.overViewParams = callback;
this.refundParams = callback;
this.refundParams.pageNumber = 1;
this.refundParams.pageSize = 10;
},
//
async initOrderOverViewList() {
const res = await API_Goods.getOrderOverView(this.overViewParams);
if (res.success) {
this.overViewList = res.result;
}
},
//
async initOrderChartList(name) {
this.orderChart ? this.orderChart.clear() : '';
const res = await API_Goods.getOrderChart(this.orderParams);
if (res.success) {
this.chartList = res.result;
if (!this.orderChart) {
this.orderChart = new Chart({
container: 'orderChart',
autoFit: true,
height: 500,
padding: [70, 70, 70, 70]
});
}
this.initOrderChart(); //
}
},
//
async getOrderList() {
let res;
if (this.orderOrRefund == 1) {
//
res = await API_Goods.statisticsOrderList(this.refundParams);
} else {
// 退
res = await API_Goods.statisticsOrderRefundList(this.refundParams);
}
if (res.success) {
this.showRecords = true;
this.data = res.result.records;
this.columns = this.orderOrRefund == 1 ? this.orderColumns : this.refundColumns;
this.total = res.result.total;
}
},
//
initBaseParams() {
const data = new Date();
this.getOrderList();
this.orderParams.year = data.getFullYear();
this.overViewParams.year = data.getFullYear();
}
},
mounted() {
this.initBaseParams();
}
};
</script>
<style scoped lang="scss">
.active {
color: #ff5c58;
position: relative;
&::before {
content: '';
position: absolute;
width: 100%;
height: 3px;
bottom: -5px;
left: 0;
background: #ff5c58;
}
}
.breadcrumb {
span {
cursor: pointer;
}
}
.page-col {
text-align: right;
margin: 10px 0;
}
.wrapper {
padding-bottom: 200px;
}
.page {
text-align: right;
margin: 20px 0;
}
#overViewChart {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
position: relative;
margin-left: 20px;
> .leftTopTips {
position: absolute;
top: 68px;
left: -2px;
width: 85px;
text-align: center;
background: rgb(255, 255, 255);
z-index: 1;
padding: 5px 0px;
}
> .leftBottomTips {
position: absolute;
bottom: 100px;
left: -2px;
width: 85px;
text-align: center;
background: rgb(255, 255, 255);
z-index: 1;
padding: 5px 0px;
}
> .rightTips {
position: absolute;
bottom: 240px;
right: 0px;
width: 85px;
text-align: center;
background: rgb(255, 255, 255);
z-index: 1;
padding: 5px 0px;
}
> .rightBorder {
width: 110px;
position: absolute;
top: 30px;
right: 40px;
border: 2px solid #d9d9d9;
border-left: 0;
height: 280px;
}
> .leftTopBorder {
border: 2px solid #d9d9d9;
height: 118px;
width: 56px;
position: absolute;
left: 40px;
top: 30px;
border-right: 0;
}
> .leftBottomBorder {
width: 108px;
border: 2px solid #d9d9d9;
height: 150px;
position: absolute;
bottom: 45px;
left: 40px;
border-right: 0;
}
> .block {
height: 0px;
border-left: 30px solid transparent;
border-right: 30px solid transparent;
position: relative;
background: rgb(255, 255, 255);
z-index: 1;
position: relative;
}
> .block:nth-of-type(1) {
width: 240px;
border-top: 90px solid #ff4646;
> .box {
left: -30px;
top: -90px;
width: 240px;
height: 90px;
}
}
> .block:nth-of-type(2) {
width: 172px;
border-top: 100px solid #ff8585;
margin-top: 10px;
> .box {
left: -29px;
top: -100px;
width: 172px;
height: 100px;
}
}
> .block:nth-of-type(3) {
width: 100px;
margin-top: 10px;
border-top: 150px solid #ffb396;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
> .box {
left: -50px;
top: -150px;
width: 100px;
height: 120px;
z-index: 2;
}
}
.box {
color: #fff;
position: absolute;
z-index: 2;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
> span {
font-size: 16px;
font-weight: bold;
}
}
}
.transaction-item {
margin: 10px 0;
}
h4 {
margin: 0 0 20px 0;
}
.transactionList {
flex: 7;
padding: 0 20px;
}
.shap {
width: 400px;
margin-left: 20px;
margin-top: 50px;
}
.transaction-card {
height: 120px;
border-radius: 0.4em;
display: flex;
background: #f3f5f7;
}
.card-item-label {
font-weight: bold;
font-size: #666;
font-size: 15px;
margin-bottom: 10px;
}
.card-item-value {
font-size: 15px;
font-weight: bold;
color: #ff5c58;
}
.card-item {
height: 100%;
width: 20%;
align-items: center;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.order-col {
display: flex;
> div {
margin-right: 8px;
padding: 16px;
font-size: 15px;
}
}
.order-list {
display: flex;
}
.tips {
margin: 0 8px;
}
.card {
margin-bottom: 10px;
}
</style>

View File

@ -0,0 +1,153 @@
<template>
<div class="wrapper">
<div class="shop">
<h3>订单详情</h3>
<div class="shop-item">
<div class="label-item">
<span>订单来源</span>
<span>{{ res.clientType || clientTypeWay }}</span>
</div>
<div class="label-item">
<span>订单状态</span>
<span>{{ orderStatusList[res.orderStatus] }}</span>
</div>
<div class="label-item">
<span>付款状态</span>
<span>{{ res.payStatus == 'UNPAID' ? '未付款' : res.payStatus == 'PAID' ? '已付款' : '' }}</span>
</div>
<div class="label-item">
<span>支付时间</span>
<span>{{ res.paymentTime || '暂无' }}</span>
</div>
<div class="label-item">
<span>支付方式</span>
<span
>{{ res.paymentMethod == 'ONLINE' ? '在线支付' : ''
}}{{ res.paymentMethod == 'ALIPAY' ? '支付宝' : res.paymentMethod == 'BANK_TRANSFER' ? '银行卡' : '' || '暂无' }}</span
>
</div>
</div>
<div class="shop-item">
<div class="label-item">
<span>用户名</span>
<span>{{ res.memberName }}</span>
</div>
<div class="label-item">
<span>店铺名称</span>
<span>{{ res.storeName }}</span>
</div>
<div class="label-item">
<span>创建时间</span>
<span>{{ res.createTime }}</span>
</div>
</div>
<h3>商品详情</h3>
<div class="shop-item">
<div class="goods-item" @click="handleClickGoods(item.goodsId)" v-for="(item, index) in res.orderItems" :key="index">
<div class="goods-img">
<img class="img" :src="item.image" alt="" />
</div>
<div class="goods-title">
<div>{{ item.name }}</div>
<div>{{ 'x' + item.num }}</div>
<div class="goods-price">{{ res.flowPrice || unitPrice('¥') }}</div>
</div>
</div>
</div>
<div class="count-price">
<div class="label-item">
<span>总价格</span>
<span class="flowPrice">
<priceColorScheme :value="row.flowPrice" :color="$mainColor"></priceColorScheme>
</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
orderStatusList: {
//
UNDELIVERED: '待发货',
UNPAID: '未付款',
PAID: '已付款',
DELIVERED: '已发货',
CANCELLED: '已关闭',
COMPLETED: '已完成',
TAKE: '已完成'
}
};
},
props: ['res'],
methods: {
handleClickGoods(id) {
this.$router.push({
query: { id },
path: '/goods/goods-info/goodsDetail'
});
}
}
};
</script>
<style lang="scss" scoped>
.shop {
padding: 10px 0;
background: #fff;
}
.shop-item {
display: flex;
flex-wrap: wrap;
}
h3 {
margin: 20px 16px;
font-size: 18px;
}
.goods-price {
font-size: 18px;
color: red;
}
.goods-item {
display: flex;
width: 100%;
margin: 16px;
cursor: pointer;
}
.count-price {
display: flex;
justify-content: flex-end;
align-items: center;
}
.flowPrice {
font-size: 24px;
color: red;
}
.goods-title {
margin: 0 16px;
display: flex;
flex-direction: column;
justify-content: center;
font-weight: bold;
}
.img {
width: 100px;
height: 100px;
border-radius: 10px;
}
.label-item {
margin: 10px 0;
width: 33%;
padding: 8px;
align-items: center;
font-weight: bold;
display: flex;
> span:nth-child(1) {
width: 70px;
color: #999;
}
}
</style>

View File

@ -0,0 +1,177 @@
<template>
<div class="wrapper">
<div class="shop">
<h3>售后详情</h3>
<div class="shop-item">
<div class="label-item">
<span>售后类型</span>
<span>{{ serviceTypeList[res.serviceType] }}</span>
</div>
<div class="label-item">
<span>售后单状态</span>
<span>{{ serviceStatusList[res.serviceStatus] }}</span>
</div>
<div class="label-item">
<span>退款时间</span>
<span>{{ res.refundTime ? new Date(res.refundTime).getTime() / 1000 || unixToDate : '暂无' }}</span>
</div>
<div class="label-item">
<span>申请退款金额</span>
<span>
<priceColorScheme :value="res.applyRefundPrice" :color="$mainColor"></priceColorScheme>
</span>
</div>
<div class="label-item">
<span>商家备注</span>
<span>{{ res.auditRemark || '暂无' }}</span>
</div>
<div class="label-item">
<span>申请原因</span>
<span>{{ res.reason || '暂无' }}</span>
</div>
</div>
<div class="shop-item">
<div class="label-item">
<span>用户名</span>
<span>{{ res.memberName }}</span>
</div>
<div class="label-item">
<span>店铺名称</span>
<span>{{ res.storeName }}</span>
</div>
<div class="label-item">
<span>创建时间</span>
<span>{{ res.createTime }}</span>
</div>
</div>
<h3>商品详情</h3>
<div class="shop-item">
<div @click="handleClickGoods(res.goodsId)" class="goods-item">
<div class="goods-img">
<img class="img" :src="res.goodsImage" alt="" />
</div>
<div class="goods-title">
<div>{{ res.goodsName }}</div>
<div>{{ 'x' + res.num }}</div>
<div class="goods-price">{{ res.flowPrice || unitPrice('¥') }}</div>
</div>
</div>
</div>
<div class="count-price">
<div class="label-item">
<span>实际退款金额</span>
<span class="flowPrice">{{ res.flowPrice || unitPrice('¥') }}</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
orderStatusList: {
//
UNDELIVERED: '待发货',
UNPAID: '未付款',
PAID: '已付款',
DELIVERED: '已发货',
CANCELLED: '已关闭',
COMPLETED: '已完成',
TAKE: '已完成'
},
//
serviceTypeList: {
CANCEL: '取消',
RETURN_GOODS: '退货',
EXCHANGE_GOODS: '换货',
RETURN_MONEY: '退款'
},
serviceStatusList: {
APPLY: '申请售后',
PASS: '通过售后',
REFUSE: '拒绝售后',
BUYER_RETURN: '买家退货,待卖家收货',
SELLER_RE_DELIVERY: '商家换货/补发',
SELLER_CONFIRM: '卖家确认收货',
SELLER_TERMINATION: '卖家终止售后',
BUYER_CONFIRM: '买家确认收货',
BUYER_CANCEL: '买家取消售后',
WAIT_REFUND: '等待平台退款',
COMPLETE: '完成售后'
}
};
},
props: ['res'],
methods: {
handleClickGoods(id) {
this.$router.push({
query: { id },
path: '/goods/goods-info/goodsDetail'
});
}
}
};
</script>
<style lang="scss" scoped>
.shop {
padding: 5px 0;
background: #fff;
}
.shop-item {
display: flex;
flex-wrap: wrap;
}
h3 {
margin: 20px 16px;
font-size: 18px;
}
.goods-price {
font-size: 18px;
color: red;
}
.goods-item {
display: flex;
width: 100%;
margin: 16px;
cursor: pointer;
}
.count-price {
display: flex;
justify-content: flex-end;
align-items: center;
}
.flowPrice {
font-size: 24px;
color: red;
}
.goods-title {
margin: 0 16px;
display: flex;
flex-direction: column;
justify-content: center;
font-weight: bold;
}
.img {
width: 100px;
height: 100px;
border-radius: 10px;
}
.label-item {
margin: 10px 0;
width: 33%;
padding: 8px;
align-items: center;
font-weight: bold;
display: flex;
> span:nth-child(1) {
width: 90px;
color: #999;
}
> span:nth-child(2) {
text-align: left;
white-space: wrap;
}
}
</style>

View File

@ -0,0 +1,223 @@
<template>
<div class="wrapper">
<!-- <Affix :offset-top="100"> -->
<el-card shadow="hover">
<affixTime :closeShop="true" @selected="clickBreadcrumb" />
</el-card>
<!-- </Affix> -->
<el-card shadow="hover">
<div>
<h4>流量概况</h4>
</div>
<div class="box">
<div class="box-item">
<div>访客数UV</div>
<div>
{{ uvs || 0 }}
</div>
</div>
<div class="box-item">
<div>浏览量PV</div>
<div>
{{ pvs || 0 }}
</div>
</div>
</div>
</el-card>
<el-card shadow="hover">
<div>
<h4>流量趋势</h4>
<div id="orderChart"></div>
</div>
</el-card>
<el-card shadow="hover">
<div>
<h4>会员流量报表</h4>
<el-table :data="data" border empty-text="暂无数据">
<el-table-column label="日期" prop="date" align="center" />
<el-table-column label="浏览量" prop="pvNum" align="center" />
<el-table-column label="访客量" prop="uvNum" align="center" />
</el-table>
</div>
</el-card>
</div>
</template>
<script>
import affixTime from '@/components/affix-time';
import * as API_Member from '@/api/member';
import { Chart } from '@antv/g2';
export default {
components: { affixTime },
data() {
return {
uvs: 0, // 访
pvs: 0, //
dateList: [
//
{
title: '今天',
selected: false,
value: 'TODAY'
},
{
title: '昨天',
selected: false,
value: 'YESTERDAY'
},
{
title: '过去7天',
selected: true,
value: 'LAST_SEVEN'
},
{
title: '过去30天',
selected: false,
value: 'LAST_THIRTY'
}
],
orderChart: '', //
params: {
//
searchType: 'LAST_SEVEN',
year: '',
month: ''
},
columns: [
{
key: 'date',
title: '日期'
},
{
key: 'pvNum',
title: '浏览量'
},
{
key: 'uvNum',
title: '访客数'
}
],
data: [] // ,
};
},
watch: {
params: {
handler(val) {
this.uvs = 0;
this.pvs = 0;
this.$nextTick(() => {
this.init();
});
},
deep: true
}
},
methods: {
//
initChart() {
const uv = [];
const pv = [];
this.data.forEach((item) => {
uv.push({
date: item.date,
uvNum: item.uvNum,
title: '访客数UV',
pv: item.uvNum
});
pv.push({
date: item.date,
pvNum: item.pvNum,
pv: item.pvNum,
title: '浏览量PV'
});
});
const data = [...uv, ...pv];
this.orderChart.data(data);
this.orderChart.scale({
activeQuantity: {
range: [0, 1],
nice: true
}
});
this.orderChart.tooltip({
showCrosshairs: true,
shared: true
});
this.orderChart.line().position('date*pv').color('title').label('pv').shape('smooth');
this.orderChart.point().position('date*pv').color('title').label('pv').shape('circle').style({
stroke: '#fff',
lineWidth: 1
});
this.orderChart.area().position('date*pv').color('title').shape('smooth');
this.orderChart.render();
},
//
clickBreadcrumb(item, index) {
const callback = JSON.parse(JSON.stringify(item));
this.params = callback;
},
//
init() {
this.orderChart ? this.orderChart.clear() : '';
API_Member.getStatisticsList(this.params).then((res) => {
if (res.result) {
this.data = res.result;
res.result.forEach((item) => {
this.uvs += item.uvNum;
this.pvs += item.pvNum;
});
if (!this.orderChart) {
this.orderChart = new Chart({
container: 'orderChart',
autoFit: true,
height: 500,
padding: [70, 70, 70, 70]
});
}
this.initChart();
}
});
}
},
mounted() {
this.init();
}
};
</script>
<style scoped lang="scss">
.table {
margin-top: 10px;
}
.box-item {
display: flex;
flex-direction: column;
width: 25%;
font-weight: bold;
justify-content: center;
> div {
margin: 4px;
}
}
.box {
background: rgb(250, 250, 250);
padding: 10px;
margin-top: 10px;
display: flex;
}
.card {
margin-bottom: 10px;
}
</style>

View File

@ -118,7 +118,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="10">
<el-form-item label="比例:" :prop="'templateDetailList[' + index + '].rate'"> <el-form-item label="店铺比例:" :prop="'templateDetailList[' + index + '].rate'">
<el-input v-model="item.rate" type="text" placeholder="请输入0.00" clearable /> <el-input v-model="item.rate" type="text" placeholder="请输入0.00" clearable />
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -144,7 +144,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="比例:" :prop="'constdfrom[' + index + '].rate'"> <el-form-item label="店铺比例:" :prop="'constdfrom[' + index + '].rate'">
<el-input v-model="item.rate" type="text" placeholder="请输入0.00" clearable /> <el-input v-model="item.rate" type="text" placeholder="请输入0.00" clearable />
</el-form-item> </el-form-item>
</el-col> </el-col>